import sys
NeededPaths=['/opt/coopip/djangoproject/include', '/opt/coopip/djangoproject', '/opt/cis', '/opt/oms/', '/opt/amr', '/opt/gis','/opt/gos', '/opt']
for path in NeededPaths:
    if(not path in sys.path):
        sys.path.append(path)
import httplib
import os, csv, shutil, datetime, time, reportlab, djangoproject.settings, datetime
from django.contrib.gis.db import models
from django.contrib.gis.geos import *
import django.db
from django.http import HttpResponse
from django.contrib.gis import admin
import _mysql as mysql
from django.db import transaction
import django, urllib
import COOPIP_Form
from ftplib import FTP
import cPickle
import amrcache
import CIS, AMR, GIS, GOS  #Comment this out before doing a syncdb
import Web


class Meter(models.Model):
    """COOPIP Meter Objects, from AMR.  Point Information from the GIS.  ServiceOrder Information from the CIS"""
    using='default'
    SerialNumber=models.TextField(db_column='serialnumber', unique=True)
    MeterModel=models.ForeignKey("MeterModel", db_column='metermodel')
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    LastTCReadAttemptDateTime=models.DateTimeField(db_column='lasttcreadattemptdatetime', null=True, blank=True)

    LastAMRTCReadDateTime=models.DateTimeField(db_column='lastamrtcreaddatetime', null=True, blank=True) #this datetime comes from a tc read object on the amr server and is used for comparison purposes only

    LastPingOutDateTime=models.DateTimeField(db_column='lastpingoutdatetime', null=True, blank=True)
    LastPingResponseDateTime=models.DateTimeField(db_column='lastpingresponsedatetime', null=True, blank=True)
    LastPingSuccessIndicator=models.NullBooleanField(db_column='lastpingsuccessindicator', null=True, blank=True)
    LastSuccessfulPingDateTime=models.DateTimeField(db_column='lastsuccessfulpingdatetime', null=True, blank=True)
    AppearedOnNotVerifiedListDateTime=models.DateTimeField(db_column='appearedonnotverifiedlistdatetime', null=True, blank=True)
    CISIdleIndicator=models.NullBooleanField(db_column='idleindicatorfromcis', null=True, blank=True)
    CISExistIndicator=models.NullBooleanField(db_column='cisexistindicator', null=True, blank=True)
    CISAccountName=models.TextField(db_column='cisaccountname', null=True, blank=True)
    Description=models.TextField(db_column='description', null=True, blank=True)  #Dispatch MeterPing Description/Notes
    RevenueProtectionReportNote=models.TextField(db_column='revenueprotectionreportnote', null=True, blank=True)
    Symbology=models.TextField(db_column='symbology', null=True, blank=True)
    IncidentSymbology=models.TextField(db_column='incidentsymbology', null=True, blank=True)
    Substation=models.ForeignKey('Substation', db_column='substation', null=True, blank=True)
    TagNumber=models.TextField(db_column='tagnumber', null=True, blank=True)
    NearbyMeters=models.TextField(db_column='nearbymeters', null=True, blank=True)
    MeterRevenueProtectionNote=models.TextField(db_column='meterrevenueprotectionnote', null=True, blank=True)
    GISCircuitNumber=models.TextField(db_column='giscircuitnumber', null=True, blank=True)
    GISCircuitName=models.TextField(db_column='giscircuitname', null=True, blank=True)
    GISSubstationName=models.TextField(db_column='gissubstationname', null=True, blank=True)
    GISPhaseCode=models.TextField(db_column='gisphasecode', null=True, blank=True)
    AMRSubstationName=models.TextField(db_column='amrsubstationname', null=True, blank=True)
    AMRCircuitName=models.TextField(db_column='amrcircuitname', null=True, blank=True)
    AMRPhaseCode=models.TextField(db_column='amrphasecode', null=True, blank=True)
    AMRSubid=models.TextField(db_column='amrsubid', null=True, blank=True)
    AMRBusid=models.TextField(db_column='amrbusid', null=True, blank=True)
    AMRFdrid=models.TextField(db_column='amrfdrid', null=True, blank=True)
    GISMapNumber=models.TextField(db_column='gismapnumber', null=True, blank=True)
    CISMapNumber=models.TextField(db_column='cismapnumber', null=True, blank=True)
    #If a meter has the Dynamic Circuit Activated Indicator, it has been updated in AMR to feed off of the ciruit listed as Current
    #When DynamicCircuitActivatedIndicator is set to false, AMR is updated with the Original Circuit Information.
    #Meters with DynamicCircuitActivatedIndicator will not pull in data from AMR and should not populate the GIS with updated circuit/phase data.
    DynamicCircuitActivatedIndicator=models.NullBooleanField(db_column='dynamiccircuitactivatedindicator', null=True, blank=True)
    DynamicCircuitOriginalAMRSubid=models.TextField(db_column='dynamiccircuitoriginalamrsubid', null=True, blank=True)
    DynamicCircuitOriginalAMRBusid=models.TextField(db_column='dynamiccircuitoriginalamrbusid', null=True, blank=True)
    DynamicCircuitOriginalAMRFdrid=models.TextField(db_column='dynamiccircuitoriginalamrfdrid', null=True, blank=True)

    DynamicCircuitCurrentAMRSubid=models.TextField(db_column='dynamiccircuitcurrentamrsubid', null=True, blank=True)
    DynamicCircuitCurrentAMRBusid=models.TextField(db_column='dynamiccircuitcurrentamrbusid', null=True, blank=True)
    DynamicCircuitCurrentAMRFdrid=models.TextField(db_column='dynamiccircuitcurrentamrfdrid', null=True, blank=True)

    DeletedIndicator=models.NullBooleanField(db_column='deletedindicator', null=True, blank=True)
    InvestigationIndicator=models.NullBooleanField(db_column='investigationindicator', null=True, blank=True)
    InvestigationStartDateTime=models.DateTimeField(db_column='investigationstartdatetime', null=True, blank=True)
    ServiceOrderIndicator=models.NullBooleanField(db_column='serviceorderindicator', null=True, blank=True)
    ServiceOrderDescription=models.TextField(db_column='serviceorderdescription', null=True, blank=True)
    SendPingToAMRIndicator=models.NullBooleanField(db_column='sendpingtoamrindicator', null=True, blank=True)
    MeterExistsInAMRIndicator=models.NullBooleanField(db_column='meterexistsinamr', null=True, blank=True)
    RecentMeterBlink=models.IntegerField(db_column='recentmeterblink', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'meter'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.SerialNumber).ljust(13) + str(self.LastPingOutDateTime).ljust(25) + str(self.LastPingSuccessIndicator).ljust(10) + str(self.CISIdleIndicator).ljust(10) +  str(self.CISExistIndicator).ljust(10)
    #UpdatePingStatus
    def Ping(self):
        self.SendPingToAMRIndicator = True
        self.save()
        if(self.Symbology == 'failed' and self.DynamicCircuitActivatedIndicator):  #Cycle the meter to the next circuit
            PossibleGISCircuits=MeterPingDynamicCircuit.GetPossibleCircuitList(self)
            FirstItem = PossibleGISCircuits[0]
            PossibleGISCircuits.append(FirstItem)  #Add the 1st item to the end of the list, allowing it to be selected as the last item in the list
            NextCircuit=None
            CurrentCircuit=GISToAMRCircuitMap.objects.filter(AMRSubid=self.DynamicCircuitCurrentAMRSubid).filter(AMRBusid=self.DynamicCircuitCurrentAMRBusid).filter(AMRFdrid=self.DynamicCircuitCurrentAMRFdrid)
            if(not CurrentCircuit):
                NextCircuit = GISToAMRCircuitMap.objects.filter(GISCircuitNumber=PossibleGISCircuits[0])[0]
            else:
                
                CurrentCircuit = CurrentCircuit[0]
                CurrentGISCircuit=CurrentCircuit.GISCircuitNumber
                for i in range(0, len(PossibleGISCircuits)): 
                    if(PossibleGISCircuits[i] == CurrentGISCircuit):
                        NextGISCircuit=PossibleGISCircuits[i + 1]
                        NextCircuit=GISToAMRCircuitMap.objects.filter(GISCircuitNumber=NextGISCircuit)
                        if(not NextCircuit):  #FIXME Indicates an Error condition, log/throw an exception
                            return
                        NextCircuit = NextCircuit[0]
                        break
            if(not NextCircuit): #FIXME check to see if this last-chance is needed
                NextCircuit = GISToAMRCircuitMap.objects.filter(GISCircuitNumber=PossibleGISCircuits[0])[0]
            self.DynamicCircuitCurrentAMRSubid = NextCircuit.AMRSubid
            self.DynamicCircuitCurrentAMRBusid = NextCircuit.AMRBusid
            self.DynamicCircuitCurrentAMRFdrid = NextCircuit.AMRFdrid
            self.save()
            self.Substation = amrcache.Substation.objects.filter(AMRsubid=self.DynamicCircuitCurrentAMRSubid)[0]
            self.save()
            self.UpdateAMRDynamicMeterCircuit()
    def UpdateAMRDynamicMeterCircuit(self):
        try:
            serialnumber=AMR.Serialnumber.objects.filter(serialnumber=self.SerialNumber)[0]
        except IndexError:
            return False
        if(self.DynamicCircuitActivatedIndicator):
            serialnumber.subid = self.DynamicCircuitCurrentAMRSubid
            serialnumber.busid = self.DynamicCircuitCurrentAMRBusid
            serialnumber.fdrid = self.DynamicCircuitCurrentAMRFdrid
        else:
            serialnumber.subid = self.DynamicCircuitOriginalAMRSubid
            serialnumber.busid = self.DynamicCircuitOriginalAMRBusid
            serialnumber.fdrid = self.DynamicCircuitOriginalAMRFdrid
        serialnumber.save()
    @staticmethod
    def UpdateMetersFromAMR():
        amrcache.AMRSerialNumber().RefreshCache()
        amrcache.Substation().RefreshCache()
        amrcache.MeterModel().RefreshCache()
        SerialNumbersUpdated=[]
        for amrmeter in amrcache.AMRSerialNumber.objects.all():
            meter=Meter.objects.filter(SerialNumber=amrmeter.serialnumber)
            if(not meter):
                meter=Meter()
                meter.Description=''
                meter.GISPhaseCode=''
                meter.GISCircuitNumber=''
                meter.RevenueProtectionReportNote=''
                meter.CISAccountName=''
                meter.SerialNumber=amrmeter.serialnumber
            else:
                meter = meter[0]
            if(meter.DynamicCircuitActivatedIndicator):  #Do not update meters from AMR that may have had their circuits modified for back-feeding purposes
                continue
            if(amrmeter.subid):
                meter.Substation=amrcache.Substation.objects.filter(AMRsubid=amrmeter.subid).get()
                meter.AMRSubstationName=amrmeter.substationname
                meter.AMRSubid = amrmeter.subid
                meter.AMRBusid = amrmeter.busid
                meter.AMRFdrid = amrmeter.fdrid
            else:
                meter.Substation=None
            try:
                meter.MeterModel=amrcache.MeterModel.objects.filter(Name=amrmeter.metertype)[0]
            except:
                meter.MeterModel=amrcache.MeterModel.objects.filter(Name='UNKNOWN')[0]
            if(amrmeter.phase == 1):
                meter.AMRPhaseCode='A'
            elif(amrmeter.phase == 2):
                meter.AMRPhaseCode='B'
            elif(amrmeter.phase == 3):
                meter.AMRPhaseCode='C'
            else:
                meter.AMRPhaseCode=''
            meter.AMRSubstationName=amrmeter.substationname
            meter.AMRCircuitName=amrmeter.feedername
            MeterExistsInAMRIndicator=True
            meter.save()
        for meter in Meter.objects.all():
            if(not amrcache.AMRSerialNumber.objects.filter(serialnumber=meter.SerialNumber).count()):
                meter.MeterExistsInAMRIndicator=False
                meter.save()
    @staticmethod
    def UpdateMetersFromGIS():
        for meter in Meter.objects.all():
            if(meter.DynamicCircuitActivatedIndicator):  #Do not update meters from GIS that may have had their circuits modified for back-feeding purposes
                continue
            GISMeters=GIS.Coopgissymbologymeter.objects.filter(meterdcsinumber=meter.SerialNumber)
            if(len(GISMeters) == 1):
                station=GIS.Coopgisstation.objects.filter(id=GISMeters[0].station_id)[0]
                meter.Point=station.the_geom
                meter.TagNumber=GISMeters[0].nearbytagnumber
                meter.GISCircuitNumber=GISMeters[0].circuitnumber
                meter.GISMapNumber=GISMeters[0].oldmapnumber
                meter.CISMapNumber=GISMeters[0].mapnumber
                meter.GISPhaseCode=GISMeters[0].phasecode
                try:
                    GISSubstationcircuit = GIS.Substationcircuit.objects.filter(circuitnumber=GISMeters[0].circuitnumber)[0]
                    meter.GISCircuitName = GISSubstationcircuit.amrcircuitname
                    meter.GISSubstationName = GIS.Coopgissubstation.objects.filter(id=GISSubstationcircuit.substation.id)[0].name
                except:
                    #print "No Circuit Found for:  " + str(GISMeters[0].circuitnumber)
                    meter.GISCircuitName = ''
                    meter.GISSubstationName = ''
                meter.NearbyMeters=GISMeters[0].nearbymeters
                meter.save()
            else:  #2nd Chance to populate the GIS Circuit Number based on AMR Data
                GISCircuitNumber = GISToAMRCircuitMap.GetGISCircuitNumber(meter.AMRSubid, meter.AMRBusid, meter.AMRFdrid)
                if(not GISCircuitNumber == 'UNKNOWN'):
                    meter.GISCircuitNumber = GISCircuitNumber
                    meter.save()
    @staticmethod
    def UpdateMetersFromCIS():
        #print "Starting:  " + str(datetime.datetime.now())
        CISMeterDetailDict={}
        CISMeterDetails=list(CIS.Meterdetl.objects.all())
        #print "Completed CISMeterDetails Query:  " + str(datetime.datetime.now())
        for i in CISMeterDetails:
            CISMeterDetailDict[str(i.meter).lstrip('0')] = []
        for i in CISMeterDetails:
            CISMeterDetailDict[str(i.meter).lstrip('0')].append(i)
        #print "Completed CISMeterDetails Dictionary:  " + str(datetime.datetime.now())
        CISServiceOrderDetailDict={}
        CISServiceOrderDetails=list(CIS.Servorderdetl.objects.all().filter(deptrouteto__endswith='00').order_by('sonbr'))
        #print "Completed CISServiceOrderDetails Query:  " + str(datetime.datetime.now())

        for i in CISServiceOrderDetails:
            CISServiceOrderDetailDict[str(i.meter).lstrip('0')] = []
        for i in CISServiceOrderDetails:
            CISServiceOrderDetailDict[str(i.meter).lstrip('0')].append(i)

        #print "Completed CISServiceOrderDetails Dictionary:  " + str(datetime.datetime.now())

        for meter in Meter.objects.all():
            try:
                CISMeterDetail=CISMeterDetailDict[meter.SerialNumber]
            except:
                CISMeterDetail=[]
            if(len(CISMeterDetail) == 1):
                meter.CISIdleIndicator=(CISMeterDetail[0].idlecode == '01')
                meter.CISExistIndicator=True
                try:
                    membersepmstr=CIS.Mbrsepmstr.objects.filter(mbrsep=CISMeterDetail[0].mbrsep)[0]
                    meter.CISAccountName=membersepmstr.name
                except:
                    meter.CISAccountName = ''
                
            else:
                meter.CISIdleIndicator=False
                meter.CISExistIndicator=False
            try:
                CISServiceOrderDetail=CISServiceOrderDetailDict[meter.SerialNumber]
            except:
                CISServiceOrderDetail=[]
            if(CISServiceOrderDetail):
                meter.ServiceOrderIndicator = True
                try:
                    meter.ServiceOrderDescription=str(CISServiceOrderDetail[0].sonbr) + ':\n' +  str(CIS.Sodescmstr.objects.filter(appltype=CISServiceOrderDetail[0].appltype)[0].sodesc)
                except:
                    meter.ServiceOrderDescription=''
            else:
                meter.ServiceOrderIndicator = False
            meter.save()
        #print "Complete Updating All Meters:  " + str(datetime.datetime.now())
    @staticmethod
    def UpdateMetersAddNewServiceOrderOnlyFromCIS():
        #Limited Update method to catch meter changes that happen throughout the day, to avoid meter changes from being listed as a "fail" throughout the day on the report
        #UpdateMetersFromCIS still needs to be run to remove service orders and other CIS changes
        CISServiceOrderDetails=list(CIS.Servorderdetl.objects.all().filter(deptrouteto__endswith='00').order_by('sonbr'))
        for serviceOrder in CISServiceOrderDetails:
            CurrentSerialNumber = str(serviceOrder.meter).lstrip('0')
            CurrentMeter=Meter.objects.filter(SerialNumber=CurrentSerialNumber).exclude(ServiceOrderIndicator=True)
            if(not CurrentMeter):
                continue
            CurrentMeter = CurrentMeter[0]
            CurrentMeter.ServiceOrderIndicator=True
            CurrentMeter.ServiceOrderDescription = str(serviceOrder.sonbr) + ':\n' +  str(CIS.Sodescmstr.objects.filter(appltype=serviceOrder.appltype)[0].sodesc)
            CurrentMeter.save()
            CurrentMeter.CalculateSymbology()
    @staticmethod
    def CalculateSymbologyForAllMeters():
        Meters=Meter.objects.all()
        for meter in Meters:
            meter.CalculateSymbology(Save=True)
            meter.CalculateIncidentSymbology(Save=True)
    def CalculateSymbology(self, Save=True):
        if(not self.Symbology):
            self.Symbology=''
        if(self.DeletedIndicator):
            self.Symbology='deleted'
        elif(self.LastPingSuccessIndicator==True and self.CISExistIndicator and not self.CISIdleIndicator):
            self.Symbology='success'
            self.AppearedOnNotVerifiedListDateTime=None
        elif(self.InvestigationIndicator):
            self.Symbology='investigation'
        elif(self.ServiceOrderIndicator and not self.LastPingSuccessIndicator):
            self.Symbology='serviceorder'
        elif(self.LastPingSuccessIndicator and (not self.CISExistIndicator or self.CISIdleIndicator)):
            if(not 'activebutnotauthorized' in self.Symbology ):
                self.AppearedOnNotVerifiedListDateTime=datetime.datetime.now()
            self.Symbology='activebutnotauthorized'
        elif(self.LastPingSuccessIndicator==False and self.CISExistIndicator and not self.CISIdleIndicator):
            MeterPings=MeterPing.objects.filter(SerialNumber=self.SerialNumber).order_by('-DateTime')
            if(MeterPings):
                if(not MeterPings[0].Rstns == 0):
                    self.Symbology='amrfailure'
                elif(not MeterPings[0].Rssce == 0):
                    self.Symbology='substationfailure'
                elif(not MeterPings[0].Rsrce == 0):
                    self.Symbology='failed'
                else:
                    self.Symbology='success'
            else:
                self.Symbology='failed'
            self.AppearedOnNotVerifiedListDateTime=None
        else:
            self.Symbology='notactive'
            self.AppearedOnNotVerifiedListDateTime=None
        if(Save):
            if(self.Symbology == 'success'):  #Check to see if the meter was being backfed
                if(self.DynamicCircuitActivatedIndicator):
                    if ((not (self.DynamicCircuitCurrentAMRSubid == self.DynamicCircuitOriginalAMRSubid)) or (not (self.DynamicCircuitCurrentAMRBusid == self.DynamicCircuitOriginalAMRBusid)) or
                        (not (self.DynamicCircuitCurrentAMRFdrid == self.DynamicCircuitOriginalAMRFdrid)) ):
                        self.Symbology = 'BF-' + self.Symbology
            self.save()
    def CalculateIncidentSymbology(self, Save=True):
        if(not self.IncidentSymbology):
            self.IncidentSymbology=''
        currentOMSIncident = CurrentOMSIncident.objects.filter(MeterNumber__icontains=self.SerialNumber)
        if(currentOMSIncident):
            currentOMSIncident = currentOMSIncident[0]
            if(self.Symbology=='failed'):
                self.IncidentSymbology='failed_with_open_incident'
            elif(self.Symbology=='BF-success'):
                self.IncidentSymbology='BF-open_incident'
            else:
                self.IncidentSymbology='open_incident'
        else:
            self.IncidentSymbology=self.Symbology
        if(Save):
            self.save()
        
    def GetNearbyMeters(self):
        NearbyMeters=[]
        if(not self.NearbyMeters):
            return NearbyMeters
        NearbyStrings=self.NearbyMeters.strip('[]').split(',')
        for nearbyString in NearbyStrings:
            nearbyString=str(nearbyString).strip(" u'")
            FoundMeters=Meter.objects.filter(SerialNumber=nearbyString)
            if(FoundMeters):
                NearbyMeters.append(FoundMeters[0])
        return NearbyMeters
    def GetLastTwoReads(self):
        """Return Last Two Meter Reads in a list [DATE1, READ1, DATE2, READ2]"""
        try:
            meterRead=MeterRead.objects.filter(Meter=self)[0]
        except KeyError:
            return [None, None, None, None]
        UsageDict=meterRead.GetKWHUsageDict()
        DateKeys=UsageDict.keys()
        if(len(DateKeys) < 2):
            return [None, None, None, None]
        DateKeys.sort()
        Key1=DateKeys[len(DateKeys) - 1]
        Key2=DateKeys[len(DateKeys) - 2]
        return [Key1, UsageDict[Key1], Key2, UsageDict[Key2]]

        
 


#Possible FIXME, interfacing with tctvoltresplog table [not implemented yet]






class MeterRead(models.Model):
    """MeterRead is a clearing house for readings that come in from AMR.
       Hourly Pulse data will not be normalized.  KWH data will be normalized."""
    using='default'
    Meter=models.ForeignKey(Meter, db_column='meter')
    RawUsage=models.TextField(db_column='rawusage', null=True, blank=True) #Raw KWH Usage from COOPTalk, Dict['DateKey']=[Positive Usage, NegativeUsage, Phase, KW]

    KWHUsage=models.TextField(db_column='kwhusage', null=True, blank=True) #KWHUsage is normalized, 1 per day

    HourlyReadingForward=models.TextField(db_column='hourlyreadingforward', null=True, blank=True) #FIXME Not Calculated Yet
    HourlyReadingNet=models.TextField(db_column='hourlyreadingnet', null=True, blank=True)
    HourlyReadingReverse=models.TextField(db_column='hourlyreadingreverse', null=True, blank=True) #FIXME Not Calculated Yet

    HourlyKWHForward=models.TextField(db_column='hourlykwhforward', null=True, blank=True)  #FIXME Not Calculated Yet
    HourlyKWHNet=models.TextField(db_column='hourlykwhnet', null=True, blank=True)
    HourlyKWHReverse=models.TextField(db_column='hourlykwhreverse', null=True, blank=True) #FIXME Not Calculated Yet

    KWDemandPulse=models.TextField(db_column='kwdemandpulse', null=True, blank=True)
    KWDemand=models.TextField(db_column='kwdemand', null=True, blank=True)

    HourlyPulseToKWHConversionFactor=models.FloatField(db_column='hourlypulsetokwhconversionfactor')
    class Meta:
        db_table=u'meterread'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.id)

    @staticmethod
    def ReduceDictToLatestDates(Dict, Days):
        NewDict={}
        Keys=Dict.keys()
        Keys.sort(reverse=True)
        if(len(Keys) > Days):
            Keys=Keys[:Days]
        for key in Keys:
            NewDict[key]=Dict[key]
        return NewDict
    @staticmethod
    def ReduceToShortTimeSpan():
        Days=360
        Total = MeterRead.objects.all().count()
        for i in range(0, Total % 500):
            print "Start to Reduce Meter:  " + str(i * 500) + '  of  ' + str(Total)
            Base = 500 * i
            Top = 500 * (i + 1)
            if(Top >= Total):
                Top = Total - 1
            MeterReads=list(MeterRead.objects.all().order_by('-Meter')[Base:Top])
            for meterRead in MeterReads:
                RawUsageDict = meterRead.GetRawUsageDict()
                RawUsageDict = MeterRead.ReduceDictToLatestDates(RawUsageDict, Days)
                meterRead.RawUsage = cPickle.dumps(RawUsageDict)

                KWHUsageDict = meterRead.GetKWHUsageDict()
                KWHUsageDict = MeterRead.ReduceDictToLatestDates(KWHUsageDict, Days)
                meterRead.KWHUsage = cPickle.dumps(KWHUsageDict)

                HourlyReadingNetDict = meterRead.GetHourlyReadingNetDict()
                HourlyReadingNetDict = MeterRead.ReduceDictToLatestDates(HourlyReadingNetDict, Days)
                meterRead.HourlyReadingNet = cPickle.dumps(HourlyReadingNetDict)

                KWDemandPulseDict = meterRead.GetKWDemandPulseDict()
                KWDemandPulseDict = MeterRead.ReduceDictToLatestDates(KWDemandPulseDict, Days)
                meterRead.KWDemandPulse = cPickle.dumps(KWDemandPulseDict)

                KWDemandDict = meterRead.GetKWDemandDict()
                KWDemandDict = MeterRead.ReduceDictToLatestDates(KWDemandDict, Days)
                meterRead.KWDemand = cPickle.dumps(KWDemandDict)
                meterRead.save()


    def GetHourlyReadingNetDict(self):
        try:
            return cPickle.loads(str(self.HourlyReadingNet))
        except cPickle.UnpicklingError:
            return {}
    def GetRawUsageDict(self):
        try:
            return cPickle.loads(str(self.RawUsage))
        except cPickle.UnpicklingError:
            return {}
    def GetKWHUsageDict(self):
        try:
            return cPickle.loads(str(self.KWHUsage))
        except cPickle.UnpicklingError:
            return {}
    def GetHourlyKWHNetDict(self):
        try:
            return cPickle.loads(str(self.HourlyKWHNet))
        except cPickle.UnpicklingError:
            return {}
    def GetKWDemandPulseDict(self):
        try:
            return cPickle.loads(str(self.KWDemandPulse))
        except cPickle.UnpicklingError:
            return {}
    def GetKWDemandDict(self):
        try:
            return cPickle.loads(str(self.KWDemand))
        except cPickle.UnpicklingError:
            return {}

    def UpdateHourlyPulseToKWHConversionFactor(self):
        aMRSerialNumber=amrcache.AMRSerialNumber.objects.filter(serialnumber=self.Meter.SerialNumber)
        if(aMRSerialNumber):
            self.HourlyPulseToKWHConversionFactor = aMRSerialNumber[0].hourlyconvhmultiplier / aMRSerialNumber[0].hourlyconvhdivisor
            print "Set to:  " + str(self.HourlyPulseToKWHConversionFactor)
        else:
            print "Defaulted to 1." 
            self.HourlyPulseToKWHConversionFactor=1



    def KeyToDateTime(self, Key):
        List=Key.split('-')
        return datetime.date(year=int(List[0]), month=int(List[1]), day=int(List[2]))
    def DateTimeToKey(self, DateTime):
        return str(DateTime.year) + '-' + str(DateTime.month).rjust(2, '0') + '-' + str(DateTime.day).rjust(2, '0')
    def UpdateHourlyReadingNet(self, StartDate):
        HourlyReadingNetDict=self.GetHourlyReadingNetDict()
        Results=[]
        MissingDataIndicator=False
        Temp=[]
        AmrhourlydataCaches=amrcache.AmrhourlydataCache.objects.filter(serialnumber=self.Meter.SerialNumber).filter(logdate__gte=StartDate).order_by('logdate')
        for i in AmrhourlydataCaches:
            HourlyReadingNetDict[self.DateTimeToKey(i.logdate)] = i.GetHourlyDataList()  #Invalid Values will be listed as 'N/A'
        self.HourlyReadingNet=cPickle.dumps(HourlyReadingNetDict)
    @staticmethod
    def UpdateMeterRead(Batches = 4, BatchNumber = 1):
        """Get All Readings From COOPTalk"""
        Meters=Meter.objects.all().order_by('SerialNumber')
        NumberOfMeters = Meters.count()
        NumberOfMetersInBatch = NumberOfMeters / Batches
        StartSlice = NumberOfMetersInBatch * (BatchNumber - 1)
        if(Batches == BatchNumber):
            EndSlice = NumberOfMeters - 1
        else:
            EndSlice = NumberOfMetersInBatch * BatchNumber
        Meters=Meters[StartSlice:EndSlice]
        StartDate=datetime.datetime.today() - datetime.timedelta(days=400)
        Count = 0

        #Meters=Meter.objects.filter(SerialNumber__in=[6610601]) #, , , , 6610601])
        
        for meter in Meters:

            if not (MeterRead.objects.filter(Meter=meter).count()):
                meterRead=MeterRead()
                meterRead.Meter=meter
                meterRead.UpdateHourlyPulseToKWHConversionFactor()
                meterRead.save()
            else:
                meterRead=MeterRead.objects.filter(Meter=meter)[0]
            #FIXME Keep resetting dicts until all bugs are worked out
            meterRead.RawUsage = cPickle.dumps({})
            meterRead.KWHUsage = cPickle.dumps({})
            #*****************************************
            meterRead.UpdateRawUsage()
            meterRead.CalculateKWHUsage()
            meterRead.UpdateHourlyReadingNet(StartDate)
            meterRead.CalculateHourlyKWHNet()
            meterRead.save()
            Count = Count + 1
            if(Count % 5 == 1):
                print "Just Updated:  " + str(meterRead.Meter.SerialNumber) + '   at:  ' + str(datetime.datetime.now()) + '    Total Readings:  ' + str(len(cPickle.loads(meterRead.KWHUsage).keys()))
                django.db.reset_queries()
    def UpdateRawUsage(self):
        RawUsageDict = djangoproject.models.cooptalk.COOPTalk.GetMeterReadings(self.Meter.SerialNumber) #Format Key = Date, Value = [Positive Usage, Negative Usage, Phase]
        self.RawUsage=cPickle.dumps(RawUsageDict)
    @staticmethod
    def CalculateUsageForDay(KWHUsageDict, DateKey, PreviousPositive, PreviousNegative, CurrentPositive, CurrentNegative):
        #Check to see whether the meter has flipped its counter
        #if(DateKey in ['2012-11-13', '2012-11-14', '2012-11-15', '2012-11-16', '2012-11-17']):
        #    print str(DateKey) + "        PreviousPositive:  " + str(PreviousPositive) +'     Current Positive:  ' + str(CurrentPositive)
           
        if(PreviousPositive > CurrentPositive and PreviousPositive > 80000 and CurrentPositive < 20000):  #FIXME allow for meters with different counter totals
            PositiveKWH=(100000 - PreviousPositive) + CurrentPositive
        else:
            PositiveKWH = CurrentPositive - PreviousPositive
        if(PreviousNegative > CurrentNegative and PreviousNegative > 80000 and CurrentNegative < 20000):
            NegativeKWH=(100000 - PreviousNegative) + CurrentNegative
        else:
            NegativeKWH = CurrentNegative - PreviousNegative
        #print str(DateKey).ljust(25) + str(PositiveKWH).ljust(25) + str(NegativeKWH).ljust(25)
        return PositiveKWH - NegativeKWH
    @staticmethod
    def PreProcessRawUsageDictKWH(RawUsageDict):
        keys = RawUsageDict.keys()
        keys.sort()
        #Fix Positive KWH Usage
        UsageList=[]
        for key in keys:
            UsageList.append(RawUsageDict[key][0])
        i = 0
        if(len(UsageList) < 4):
            return RawUsageDict  #Do not attempt to analyze a list for errors with 3 or fewer readings
        for i in range(0, len(UsageList) - 2): #If the difference between the current and next reading is greater than the difference between the current and 2 readings ahead, skip it
            if(abs(int(UsageList[i]) - int(UsageList[i + 1])) > abs(int(UsageList[i]) - int(UsageList[i + 2]))):  
                RawUsageDict[keys[i+1]][0] = RawUsageDict[keys[i]][0]
 

        #Fix Negative KWH Usage
        UsageList=[]
        for key in keys:
            UsageList.append(RawUsageDict[key][1])       
        for i in range(0, len(UsageList) - 2): #If the difference between the current and next reading is greater than the difference between the current and 2 readings ahead, skip it
            if(abs(int(UsageList[i]) - int(UsageList[i + 1])) > abs(int(UsageList[i]) - int(UsageList[i + 2]))):  
                RawUsageDict[keys[i+1]][1] = RawUsageDict[keys[i]][1]
        return RawUsageDict
            
     


    def CalculateKWHUsage(self):
        RawUsageDict=self.GetRawUsageDict()
        RawUsageDict=MeterRead.PreProcessRawUsageDictKWH(RawUsageDict)  

        KWHUsageDict=self.GetKWHUsageDict()
        DateKeys=RawUsageDict.keys()
        DateKeys.sort()
        if(not DateKeys):
            self.KWHUsage=cPickle.dumps({})
            return
        First=True
        for key in DateKeys:
            if(First):
                PreviousPositive=float(RawUsageDict[key][0])
                PreviousNegative=float(RawUsageDict[key][1])
                First=False
                continue
            KWHUsageDict[key] = MeterRead.CalculateUsageForDay(KWHUsageDict, key, PreviousPositive, PreviousNegative, float(RawUsageDict[key][0]), float(RawUsageDict[key][1]))
            PreviousPositive=float(RawUsageDict[key][0])
            PreviousNegative=float(RawUsageDict[key][1])

        #Now fill in any gaps of 1 day
        KWHUsageKeys=KWHUsageDict.keys()
        KWHUsageKeys.sort()
        First=True
        for key in KWHUsageKeys:
            if(First):
                PreviousDate=self.KeyToDateTime(key)
                PreviousKey=key
                continue
            CurrentDate=self.KeyToDateTime(key)
            DaysDifferent=(CurrentDate-PreviousDate).days
            if(DaysDifferent > 1 and DaysDifferent < 3):  #Split up the reading over 2 days equally, later on we may analyze usage patterns to increase accuracy/extend number of days
                KWHUsage=KWHUsageDict[key]
                KWHUsageDict[key] = KWHUsage / DaysDifferent
                KWHUsageDict[DateTimeToKey(CurrentDate - datetime.timedelta(days=1))] = KWHUsage / DaysDifferent
        self.KWHUsage=cPickle.dumps(KWHUsageDict)
    def CalculateHourlyKWHNet(self):
        HourlyReadingIndicator = self.Meter.MeterModel.HourlyReadingIndicator
        KWHUsageDict=self.GetKWHUsageDict()
        HourlyReadingNetDict=self.GetHourlyReadingNetDict()
        HourlyKWHNetDict={}
        #Fill in the HourlyKWHNetDict with initial KWH usage values based on HourlyReadingNet
        Keys=HourlyReadingNetDict.keys()
        Keys.sort()
        for key in Keys:
            HourlyRow=[]
            for i in HourlyReadingNetDict[key]:
                if(i == 'N/A') or not HourlyReadingIndicator:  #Set each hourly reading to N/A if hourly readings are not supported for the meter
                    HourlyRow.append('N/A')
                else:
                    HourlyRow.append(i*self.HourlyPulseToKWHConversionFactor)
            HourlyKWHNetDict[key]=HourlyRow
        #Add Keys for any missing hourly days based on KWHUsage, with 'N/A' for each usage
        Keys=KWHUsageDict.keys()
        for key in Keys:
            if(not HourlyKWHNetDict.has_key(key)):
                HourlyKWHNetDict[key]=['N/A']*24
        #Attempt to fill in any 'N/A' usages in the HourlyKWHNetDict based on the HourlyReadingNetDict [Estimate Hourly Usage]
        Keys=HourlyKWHNetDict.keys()
        Keys.sort()
        for key in Keys:
            if('N/A' in HourlyKWHNetDict[key]):
                #PreviousDaysKey=self.DateTimeToKey(self.KeyToDateTime(key) - datetime.timedelta(days=1))

                if(not KWHUsageDict.has_key(key)):  #No Daily Usage available for this day, so skip trying to fill in the 'N/A's
                    continue
                UnaccountedKWHUsage=KWHUsageDict[key]
                UnaccountedHours=0.0
                for i in HourlyKWHNetDict[key]:
                    if(not i == 'N/A'):
                        UnaccountedKWHUsage=UnaccountedKWHUsage - i
                    else:
                        UnaccountedHours = UnaccountedHours + 1.0
                #Now split this amount by the remaining hours and assign
                Count = 0
                for i in HourlyKWHNetDict[key]:
                    if(i == 'N/A'):
                        HourlyKWHNetDict[key][Count] = UnaccountedKWHUsage / UnaccountedHours
                    Count = Count + 1
        self.HourlyKWHNet=cPickle.dumps(HourlyKWHNetDict)
        self.save()
    def UpdateKWDemandPulse(self, StartDate = datetime.datetime.today() - datetime.timedelta(days=40)):
        #FIXME Get from Multispeak
        KWDemandPulseDict=self.GetKWDemandPulseDict()
        django.db.reset_queries()
        self.save()
    def UpdateKWDemand(self):
        KWDemandPulseDict=self.GetKWDemandPulseDict()
        KWDemandDict={}
        meterModel = self.Meter.MeterModel
        if(not meterModel) or (not meterModel.PulseToKWHDivisor) or (not meterModel.meterconvmit):
            self.KWDemand=cPickle.dumps(KWDemandDict)
            return
        for key in KWDemandPulseDict:
           KWDemandDict[key] = KWDemandPulseDict[key] * meterModel.PulseToKWHMultiplier / meterModel.meterconvmit / meterModel.PulseToKWHDivisor
        self.KWDemand = cPickle.dumps(KWDemandDict)
        self.save()




class UsageVariance(models.Model):
    using='default'
    Meter=models.ForeignKey(Meter, db_column='meter')
    StartDateTime=models.DateTimeField(db_column='startdatetime', null=True, blank=True)
    EndDateTime=models.DateTimeField(db_column='enddatetime', null=True, blank=True)
    DailyUsageNumberOfDays=models.IntegerField(db_column='dailyusagenumberofdays', null=True, blank=True)
    DailyUsageTotal=models.FloatField(db_column='dailyusagetotal', null=True, blank=True)
    DailyUsageAverage=models.FloatField(db_column='dailyusageaverage', null=True, blank=True)
    DailyStepAverage=models.FloatField(db_column='dailystepaverage', null=True, blank=True)
    DailyStepMaximum=models.FloatField(db_column='dailystepmaximum', null=True, blank=True)
    DailyStepPercentAverage=models.FloatField(db_column='dailysteppercentaverage', null=True, blank=True)
    DailyStepPercentMaximum=models.FloatField(db_column='dailysteppercentmaximum', null=True, blank=True)
    Description=models.TextField(db_column='description', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'usagevariance'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.Meter.SerialNumber).ljust(15)



class MeterBlink(models.Model):
    using='default'
    Meter=models.ForeignKey(Meter, db_column='meter')
    RawCount=models.TextField(db_column='rawcount', null=True, blank=True)
    #RawCount contains a cPickled Dictionary in the form:
    #RawCountDictionary={Date:  [Count]
    Statistics=models.TextField(db_column='statistics', null=True, blank=True)  #Statistics contains a cPickled Dictionary in the form: StatisticsDictionary={Date: [BlinksSincePreviousReading, NumberOfReadingsWithBlinksInRow]}
    NearbyBlinks=models.TextField(db_column='nearbymeterblinkindicator', null=True, blank=True)
    #NearbyBlinks contains a cPickled Dictionary in the form:
    #NearbyBlinksDictionary={Date:  [Meter1, Meter2]}
    BlinkCountTimeSpan=models.IntegerField(db_column='blinkcounttimespan', null=True, blank=True)
    BlinkWithNoNeighborTimeSpan=models.IntegerField(db_column='blinkwithnoneighbortimespan', null=True, blank=True)
    BlinkDaysInRowTimeSpan=models.IntegerField(db_column='blinkdaysinrowtimespan', null=True, blank=True)
    Description=models.TextField(db_column='description', null=True, blank=True)
    CISLastServiceOrderDate=models.TextField(db_column='cislastserviceorderdate', null=True, blank=True)
    CISLastServiceOrderName=models.TextField(db_column='cislastserviceordername', null=True, blank=True)
    OMSLastOutageDate=models.TextField(db_column='omslastoutagedate', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'meterblink'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.Meter.SerialNumber).ljust(15)
    def MonthNameToNumber(self, Name):
        if(Name == 'JAN'):
            return 1
        elif(Name == 'FEB'):
            return 2
        elif(Name == 'MAR'):
            return 3
        elif(Name == 'APR'):
            return 4
        elif(Name == 'MAY'):
            return 5
        elif(Name == 'JUN'):
            return 6
        elif(Name == 'JUL'):
            return 7
        elif(Name == 'AUG'):
            return 8
        elif(Name == 'SEP'):
            return 9
        elif(Name == 'OCT'):
            return 10
        elif(Name == 'NOV'):
            return 11
        elif(Name == 'DEC'):
            return 12
        else:
            raise Exception('MonthNametoNumber Had an Invalid Month Name:  ' + str(Name))
    def AddNewRawCount(self, DateTime, RawCount):
        try:
            RawCountDict=cPickle.loads(str(self.RawCount))
        except:
            RawCountDict={}
        RawCountDict[str(DateTime)] = RawCount
        self.RawCount = cPickle.dumps(RawCountDict)
    def ReduceDictToLatestDates(self, Dict, Days):
        NewDict={}
        Keys=Dict.keys()
        Keys.sort(reverse=True)
        if(len(Keys) > Days):
            Keys=Keys[:Days]
        for key in Keys:
            NewDict[key]=Dict[key]
        return NewDict
    def ReduceToShortTimeSpan(self):
        Days=360
        MeterBlinks=MeterBlink.objects.all().order_by('Meter')
        for meterblink in MeterBlinks:
            try:
                RawCountDict=cPickle.loads(str(meterblink.RawCount))
            except:
                RawCountDict={}
            try:
                StatisticsDict=cPickle.loads(str(meterblink.Statistics))
            except:
                StatisticsDict={}
            try:
                NearbyBlinksDict=cPickle.loads(str(meterblink.NearbyBlinks))
            except:
                StatisticsDict={}
            RawCountDict=self.ReduceDictToLatestDates(RawCountDict, Days)
            StatisticsDict=self.ReduceDictToLatestDates(StatisticsDict, Days)
            NearbyBlinksDict=self.ReduceDictToLatestDates(NearbyBlinksDict, Days)
            meterblink.RawCount=cPickle.dumps(RawCountDict)
            meterblink.Statistics=cPickle.dumps(StatisticsDict)
            meterblink.NearbyBlinksDict=cPickle.dumps(NearbyBlinksDict)
            meterblink.save()
    def ImportMeterBlinks(self):
        MeterBlinkImportFiles=amrcache.MeterBlinkImportFile.objects.all().exclude(FileParsedIndicator=True).order_by('FileDateTime')
        MeterBlinkDict={}
        MeterDict={}
        for meterblinkimportfile in MeterBlinkImportFiles:
            print str("Starting on file:  " + str(meterblinkimportfile.FileName)).ljust(50) + str(datetime.datetime.now())
            Lines=meterblinkimportfile.RawData.strip('\r').split('\n')
            for line in Lines:
                SerialNumber=line[:13].strip()
                meter=Meter.objects.filter(SerialNumber=SerialNumber)
                if(not meter):
                    continue
                else:
                    meter = meter[0]
                if(MeterBlinkDict.has_key(SerialNumber)):
                    pass  #This record already exists and has been loaded
                else:
                    meterBlinks=MeterBlink.objects.filter(Meter=meter)
                    if(not meterBlinks):
                        meterBlink=MeterBlink()
                        meterBlink.Meter=meter
                        meterBlink.Description=''
                        meterBlink.save()
                        MeterBlinkDict[str(meterBlink.Meter.SerialNumber)] = meterBlink
                    else:
                        MeterBlinkDict[str(meterBlinks[0].Meter.SerialNumber)] = meterBlinks[0]
                Day=int(line[13:15])
                MonthName=line[16:19]
                Year=int(line[20:24])
                RawCount=int(line[34:39])
                Month = self.MonthNameToNumber(MonthName)
                DateTime=datetime.date(Year, Month, Day)
                MeterBlinkDict[SerialNumber].AddNewRawCount(DateTime, RawCount)
            meterblinkimportfile.FileParsedIndicator=True
            meterblinkimportfile.save()
            print str("Finished File:  " + str(meterblinkimportfile.FileName)).ljust(50) + str(datetime.datetime.now())
        print str("Saving Changes:  ") +  str(datetime.datetime.now())
        for key in MeterBlinkDict.keys():
            MeterBlinkDict[key].save()
        print str("Saving Complete:  ") + str(datetime.datetime.now())
    def CalculateStatistics(self):
        MeterBlinks=MeterBlink.objects.all().order_by('Meter')
        for meterblink in MeterBlinks:
            RawCountDict=cPickle.loads(str(meterblink.RawCount))
            StatisticsDict={}
            DateList=RawCountDict.keys()
            DateList.sort()
            FirstDate=True
            BlinksInRow=0
            for date in DateList:
                if(FirstDate):
                    PreviousCountReading=RawCountDict[date]
                    FirstDate=False
                    continue
                BlinksSincePreviousReading=RawCountDict[date] - PreviousCountReading
                if(BlinksSincePreviousReading):
                    BlinksInRow = BlinksInRow + 1
                else:
                    BlinksInRow = 0
                StatisticsDict[date]=[BlinksSincePreviousReading, BlinksInRow]
                PreviousCountReading=RawCountDict[date]
            meterblink.Statistics=cPickle.dumps(StatisticsDict)
            meterblink.save()

    def CalculateNearbyBlinks(self):
        MeterBlinks=MeterBlink.objects.all().select_related('Meter').order_by('Meter')
        AllMeterBlinkStatisticsDict={}
        for meterblink in MeterBlinks:
            StatisticsDict=cPickle.loads(str(meterblink.Statistics))
            AllMeterBlinkStatisticsDict[meterblink.Meter.SerialNumber]=StatisticsDict
        for meterblink in MeterBlinks:
            StatisticsDict=AllMeterBlinkStatisticsDict[meterblink.Meter.SerialNumber]
            NearbyBlinks={}
            NearbyMeters=meterblink.Meter.GetNearbyMeters()
            for date in StatisticsDict.keys():
                NearbyMetersThatBlinked=[]
                for meter in NearbyMeters:
                    try:
                        if(AllMeterBlinkStatisticsDict[meter.SerialNumber][date][0]):
                            NearbyMetersThatBlinked.append(meter.SerialNumber)
                    except:
                        pass
                NearbyBlinks[date] = NearbyMetersThatBlinked
            meterblink.NearbyBlinks = cPickle.dumps(NearbyBlinks)
            meterblink.save()
    def CalculateTimeSpanFields(self):
        MeterBlinks=MeterBlink.objects.all().order_by('Meter')
        Today=datetime.datetime.now()
        StartDate=Today-datetime.timedelta(days=7)
        DateList=[]  #Dates in YYYY-MM-DD Format
        while(StartDate < Today):
            DateList.append(str(StartDate.year) + '-' + str(StartDate.month).rjust(2, '0') + '-' + str(StartDate.day).rjust(2, '0'))
            StartDate = StartDate + datetime.timedelta(days=1)
        for meterblink in MeterBlinks:
            StatisticsDict=cPickle.loads(str(meterblink.Statistics))
            NearbyBlinksDict=cPickle.loads(str(meterblink.NearbyBlinks))
            BlinkCountTimeSpan=0
            BlinkWithNoNeighborTimeSpan=0
            BlinkDaysInRowTimeSpan=0
            for date in DateList:
                try:
                    BlinkCountTimeSpan=StatisticsDict[date][0] + BlinkCountTimeSpan
                except KeyError:
                    pass
                try:
                    if(StatisticsDict[date][0] and not NearbyBlinksDict[date]):
                        BlinkWithNoNeighborTimeSpan = BlinkWithNoNeighborTimeSpan + 1
                except KeyError:
                    pass
                try:
                    if(StatisticsDict[date][1] > BlinkDaysInRowTimeSpan):
                        BlinkDaysInRowTimeSpan = BlinkDaysInRowTimeSpan + 1
                except KeyError:
                    pass
            meterblink.BlinkDaysInRowTimeSpan=BlinkDaysInRowTimeSpan
            meterblink.BlinkWithNoNeighborTimeSpan=BlinkWithNoNeighborTimeSpan
            meterblink.BlinkCountTimeSpan=BlinkCountTimeSpan
            meterblink.save()
    def GetSpecifiedRawCounts(self, Dates, BlankDatesWithPrevious=False):
        RawCountDict=cPickle.loads(str(self.RawCount))
        Results=[]
        Previous='Unknown'
        DateList=RawCountDict.keys()
        for date in Dates:
            if(date in DateList):
                Results.append(str(RawCountDict[date]))
                Previous=str(RawCountDict[date])
            else:
                if(BlankDatesWithPrevious and not Previous == 'Unknown'):
                    Results.append(Previous)
                else:
                    Results.append('')
        return Results
    def GetMostRecentDates(self, NumberOfDates=7): #Dates in YYYY-MM-DD Format
        date=datetime.datetime.now()
        Results = []
        for i in range(0, NumberOfDates):
            Results.append(str(date.year) + '-' + str(date.month).rjust(2, '0') + '-' + str(date.day).rjust(2, '0'))
            date = date - datetime.timedelta(days=1)
        return Results
    def PrintRawCount(self):
        RawCountDict=cPickle.loads(str(self.RawCount))
        Results=[]
        DateList=RawCountDict.keys()
        DateList.sort()
        for date in DateList:
            print str(date) + '   ' + str(RawCountDict[date])
    def GetCISServiceOrderData(self):
        MeterBlinks=MeterBlink.objects.all().order_by('Meter')
        Now=datetime.datetime.now()
        StartDate=Now-datetime.timedelta(days=90)
        StartDateCISString=str(StartDate.year)[2:] + str(StartDate.month).rjust(2, '0') + str(StartDate.day).rjust(2, '0')
        CISServiceOrders=list(CIS.Servorderdetl.objects.filter(mchdte__gte=StartDateCISString).order_by('-mchdte'))
        ServiceOrderDict={}
        for serviceOrder in CISServiceOrders:
            if(not serviceOrder.meter.lstrip('0') in ServiceOrderDict.keys() and not serviceOrder.mchdte[0] == '9'):
                ServiceOrderDict[str(serviceOrder.meter).lstrip('0')] = serviceOrder
        for meterblink in MeterBlinks:
            if(ServiceOrderDict.has_key(meterblink.Meter.SerialNumber)):
                meterblink.CISLastServiceOrderDate=ServiceOrderDict[meterblink.Meter.SerialNumber].mchdte
                meterblink.CISLastServiceOrderName=str(ServiceOrderDict[meterblink.Meter.SerialNumber].sonbr) + ':\n' +  str(CIS.Sodescmstr.objects.filter(appltype=ServiceOrderDict[meterblink.Meter.SerialNumber].appltype)[0].sodesc)
            else:
                if(meterblink.CISLastServiceOrderDate == ''):
                    continue
                meterblink.CISLastServiceOrderDate=''
                meterblink.CISLastServiceOrderName=''
            meterblink.save()
    def GetOMSOutageHistory(self):
        MeterBlinks=MeterBlink.objects.all().order_by('Meter')
        Now=datetime.datetime.now()
        #FIXME     OMSLastOutageDate=models.TextField(db_column='omslastoutagedate', null=True, blank=True)
        #Not Implemented yet, need to pull from customer table to get an account number, then join with the consumeroutagetable.
        #Most likely create a dict for these tables locally to avoid thousands of small queries.




    
class Vehicle(models.Model):
    """Intended for quick profiling on dispatch display"""
    using='default'
    #static
    Name=models.TextField(db_column='name',null=True, blank=True)
    Number=models.TextField(db_column='number',null=True, blank=True)
    #non-static; updated periodically from vehicle logs
    LatestLogDateTime=models.DateTimeField(db_column='latestlogdatetime', null=True, blank=True)
    Speed=models.IntegerField(db_column='speed',null=True, blank=True)
    Bearing=models.IntegerField(db_column='bearing',null=True, blank=True)
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    Symbology=models.TextField(db_column='symbology', null=True, blank=True)
    NearestRoadName=models.TextField(db_column='nearestroadname', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'vehicle'
        app_label='djangoproject'
    @staticmethod
    def UpdateFromLogs():
        #if vehicle doesn't already exist, create it
        for number in VehicleLog.objects.values_list('VehicleNumber', flat=True).distinct():
            if Vehicle.objects.filter(Number=number).count():
                continue
            try:
                test = int(number)
                new = Vehicle()
                new.Name = number
                new.Number = number
                new.save()
            except:
                pass #must be integer; malformed packets usually don't parse, but some come across with invalid data
        for vehicle in Vehicle.objects.all():
            LatestLog = VehicleLog.objects.filter(VehicleNumber=vehicle.Number).order_by('-CreateDateTime')[0]
            if LatestLog.CreateDateTime == vehicle.LatestLogDateTime:
                continue
            if not LatestLog.Point:
                continue
            vehicle.LatestLogDateTime = LatestLog.CreateDateTime
            vehicle.Speed = LatestLog.Speed
            vehicle.Bearing = LatestLog.Bearing
            vehicle.Point = LatestLog.Point
            vehicle.save()
        Current = datetime.datetime.now()
        for vehicle in Vehicle.objects.all():
            vehicle.CalculateSymbology(Current)
            vehicle.FindNearestRoad()
    def CalculateSymbology(self, CurrentDateTime):
        if (CurrentDateTime - self.LatestLogDateTime).total_seconds() > 360:
            self.Symbology = 'notreading'
        elif self.Speed == 0:
            self.Symbology = 'stopped'
        else:
            self.Symbology = 'moving'
        self.save()
    def FindNearestRoad(self):
        #16000/50000 roads lack names
        #Road = GIS.Coopgisroad.objects.exclude(name='').distance(self.Point).order_by('distance')[0]
        Road = GIS.Coopgisroad.objects.distance(self.Point).order_by('distance')[0]
        self.NearestRoadName = Road.name
        self.save()
          
             
    
class VehicleLog(models.Model):
    """Represent individual TAIP transmissions sent by AVL devices"""
    using='default'
    CreateDateTime=models.DateTimeField(db_column='createdatetime', null=True, blank=True)
    AVLSystemLogid=models.IntegerField(db_column='avlsystemlogid', null=True, blank=True)
    Speed=models.IntegerField(db_column='speed',null=True, blank=True)
    Bearing=models.IntegerField(db_column='bearing',null=True, blank=True)
    VehicleNumber=models.TextField(db_column='vehiclenumber',null=True, blank=True)
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    SymbologyStopStartState=models.TextField(db_column='symbologystopstartstate',null=True, blank=True)
    SymbologyVisible=models.TextField(db_column='symbologyvisible',null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'vehiclelog'
        app_label='djangoproject'
    @staticmethod
    def ImportLogs(All = False):
        if All:
            HighestCOOPWebid = 0
        else:
            HighestCOOPWebid = VehicleLog.objects.all().order_by('-AVLSystemLogid')[0].AVLSystemLogid
        Unimported = Web.AVLSystemLog.objects.filter(id__gt=HighestCOOPWebid).order_by('id')
        for i in Unimported:
            new = VehicleLog()
            new.CreateDateTime = datetime.datetime.now()
            new.AVLSystemLogid = i.id
            new.Point = i.Point
            new.Speed = i.Speed
            new.Bearing = i.Bearing
            new.VehicleNumber = i.VehicleNumber
            new.save()
    def CalculateSymbologyStopStartState(self):
        Timeout = 1800
        DistanceThreshold = 500 #feet
        Logs = VehicleLog.objects.filter(VehicleNumber=self.VehicleNumber).filter(CreateDateTime__lt=self.CreateDateTime).exclude(Point__isnull=True).order_by('-CreateDateTime')
        if not Logs.count():
            return# 'No log'
        Log = Logs[0]
        Sentinel = False
        if (self.CreateDateTime - Log.CreateDateTime).total_seconds() >= Timeout:
            self.SymbologyStopStartState = 'start'
            self.save()
            Log.SymbologyStopStartState= 'stop'
            Log.save()
            Sentinel=True
        if (not self.Speed > 0) and self.Point.distance(Log.Point) > DistanceThreshold:
            self.SymbologyStopStartState = 'locationchange'
            self.save()
            Sentinel=True
        if not Sentinel:
            self.SymbologyStopStartState = ''
            self.save()
        #return 'Succeeded'
    """
    reinstate if users require ability to recalculate on the fly
    @staticmethod
    def CalculateSymbology(VehicleNumber='', Start=None, End=None, Timeout=1800):
        if VehicleNumber and Start and End:
            Logs = list(VehicleLog.objects.filter(VehicleNumber = VehicleNumber).filter(CreateDateTime__gte=Start).filter(CreateDateTime__lte=End).order_by('CreateDateTime'))
        #    LogWrap.append(Logs)
        elif VehicleNumber:
            Logs = list(VehicleLog.objects.filter(VehicleNumber = VehicleNumber).order_by('CreateDateTime'))
        #    LogWrap.append(Logs)
        #else:
        #    for vehicle in Vehicle.objects.all():
        #        Logs = list(VehicleLog.objects.filter(VehicleNumber = vehicle.Number).order_by('CreateDateTime'))
        #        LogWrap.append(Logs)
        #for Logs in LogWrap:
        for log in Logs:
            log.SymbologyTransmission = ''
            log.save()
        for i in xrange(1, len(Logs)):
            log = Logs[i]
            Previous = Logs[i-1]
            Timeout = 1800 #seconds
    """            



class CurrentOMSIncident(models.Model):
    using='default'
    Recordid=models.TextField(db_column='recordid', null=True, blank=True)
    Incidentid=models.TextField(db_column='incidentid', null=True, blank=True)
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    Status=models.TextField(db_column='status', null=True, blank=True)
    OutageType=models.TextField(db_column='outagetype', null=True, blank=True)
    MeterNumber=models.TextField(db_column='meternumber', null=True, blank=True)
    MapNumber=models.TextField(db_column='mapnumber', null=True, blank=True)
    class Meta:
        db_table=u'currentomsincident'
        app_label='djangoproject'
    def CreateLog(self):
        IncidentExists = OMSIncidentHistory.objects.filter(Recordid = self.Recordid)
        if IncidentExists.count():
            return
        new = OMSIncidentHistory()
        new.Recordid = self.Recordid
        new.Incidentid = self.Incidentid
        new.Point = self.Point
        new.Status = self.Status
        new.OutageType = self.OutageType
        new.MeterNumber = self.MeterNumber
        new.MapNumber = self.MapNumber
        new.CreateDateTime = datetime.datetime.now()
        new.save()

class OMSIncidentHistory(models.Model):
    using='default'
    Recordid=models.TextField(db_column='recordid', null=True, blank=True)
    Incidentid=models.TextField(db_column='incidentid', null=True, blank=True)
    Point=models.PointField(db_column='the_geom', srid=2273, null=True, blank=True)
    Status=models.TextField(db_column='status', null=True, blank=True)
    OutageType=models.TextField(db_column='outagetype', null=True, blank=True)
    MeterNumber=models.TextField(db_column='meternumber', null=True, blank=True)
    MapNumber=models.TextField(db_column='mapnumber', null=True, blank=True)
    CreateDateTime=models.DateTimeField(db_column='createdatetime', null=True, blank=True)
    AssignedTo=models.TextField(db_column='assignedto', null=True, blank=True)
    AssignedDateTime=models.DateTimeField(db_column='assignedtodatetime', null=True, blank=True)
    class Meta:
        db_table=u'omsincidenthistory'
        app_label='djangoproject'






class MeterSearch(models.Model):
    ''' Represents the results of several meter searches across different substations'''
    using='default'
    SerialNumber=models.BigIntegerField(db_column='serialnumber')
    Substation=models.ForeignKey('Substation', db_column='substation', related_name='Substation', null=True, blank=True)
    PriorityLevel=models.IntegerField(db_column='prioritylevel')
    PrioritySubstation=models.ForeignKey('Substation', db_column='prioritysubstation', related_name='PrioritySubstation',null=True, blank=True)
    StartDateTime=models.DateTimeField(db_column='startdatetime')
    OutToAMRCount=models.IntegerField(db_column='outtoamrcount', null=True, blank=True)
    CurrentlyOutToAMRIndicator=models.NullBooleanField(db_column='currentlyouttoamrindicator', null=True, blank=True)
    LastOutToAMRDateTime=models.DateTimeField(db_column='lastouttoamrdatetime', null=True, blank=True)  #Latest time sent out to AMR
    FinalResultMeterNotFoundIndicator=models.NullBooleanField(db_column='finalresultmeternotfoundindicator', null=True, blank=True)
    FinalResultMeterAddedIndicator=models.NullBooleanField(db_column='finalresultmeteraddedindicator', null=True, blank=True)
    FinalResultMeterResearchedIndicator=models.NullBooleanField(db_column='finalresultmeterresearchedindicator', null=True, blank=True)
    CompleteDateTime=models.DateTimeField(db_column='completedatetime', null=True, blank=True)
    SubstationNamesAlreadySearchedList=models.TextField(db_column='substationnamesalreadysearchedlist', null=True, blank=True)
    SubstationCountAlreadySearched=models.IntegerField(db_column='substationcountalreadysearched', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'metersearch'
        app_label='djangoproject'
    def AddSubstationNameToList(self, SubstationName):
        if not self.SubstationNamesAlreadySearchedList:
            LoadList=[]
            LoadList.append(SubstationName)
            self.SubstationNamesAlreadySearchedList=str(LoadList)
            self.SubstationCountAlreadySearched=1
        else:
            CurrentSubstationNamesList=self.SubstationNamesAlreadySearchedList.replace("u'",'').replace("'",'').replace('[','').replace(']','').split(',')
            CurrentSubstationNamesList.append(SubstationName.lstrip(' '))
            self.SubstationCountAlreadySearched=self.SubstationCountAlreadySearched + 1
            self.SubstationNamesAlreadySearchedList=str(CurrentSubstationNamesList)
    def UpdateMeterSearches(self):
        MeterSubstationSearches=MeterSubstationSearch.objects.filter(MeterSearchNeedsToBeUpdatedIndicator=True).order_by('CompleteDateTime')
        for meterSubstationSearch in MeterSubstationSearches:
            meterSearch=meterSubstationSearch.MeterSearch
            if(meterSubstationSearch.AMRMeterNotFoundIndicator):
                meterSearch.AddSubstationNameToList(meterSubstationSearch.Substation.Name)
                if(meterSearch.SubstationCountAlreadySearched == amrcache.Substation.objects.all().exclude(Name__iexact='UNKNOWN').count()):
                    meterSearch.CompleteDateTime=meterSubstationSearch.CompleteDateTime
                    meterSearch.FinalResultMeterNotFoundIndicator=True
                meterSearch.PrioritySubstation=None  #Search failed on this substation, so set to null so the rest of the subs can be searched
                meterSearch.CurrentlyOutToAMRIndicator=False
                meterSearch.Substation=None
                meterSearch.save()
            elif(meterSubstationSearch.AMRNewMeterFoundIndicator):
                meterSearch.FinalResultMeterAddedIndicator=meterSubstationSearch.AMRNewMeterFoundIndicator
                meterSearch.Substation=meterSubstationSearch.Substation
                meterSearch.CompleteDateTime=meterSubstationSearch.CompleteDateTime
                meterSearch.CurrentlyOutToAMRIndicator=False
                meterSearch.save()
            elif(meterSubstationSearch.AMRMeterResearchedIndicator):
                meterSearch.FinalResultMeterResearchedIndicator=True
                meterSearch.Substation=meterSubstationSearch.Substation
                meterSearch.CompleteDateTime=meterSubstationSearch.CompleteDateTime
                meterSearch.CurrentlyOutToAMRIndicator=False
                meterSearch.save()
            meterSubstationSearch.MeterSearchNeedsToBeUpdatedIndicator=False
            meterSubstationSearch.save()
    def SendMeterSearchesToAMR(self):
        amrcache.Substation().RefreshCache()
        Substations=amrcache.Substation.objects.all().exclude(Name='UNKNOWN')
        TotalSearchCap = 7
        AMRSearchCap = 7
        SearchesToGoOutCap= 7
        for substation in Substations:
            print "On Substation:  " + substation.Name
            if not AMR.Scemain.objects.filter(substatus=2).filter(subname=substation.Name).count():
                print "Substation is not online:  " + substation.Name
                continue #check sub is online in AMR

            if substation.AMRSearchesOut > AMRSearchCap:
                print str(substation.AMRSearchesOut) + '  AMR Searches Out was too high:  ' + str(substation.Name)
                continue
            COOPIPSearchesOutToAMR=MeterSubstationSearch.objects.filter(OutToAMRIndicator=True).filter(Substation=substation).count()
            COOPIPSearchesWaitingToGoOutToAMR=MeterSubstationSearch.objects.filter(WaitingToGoOutToAMRIndicator=True).filter(Substation=substation).count()
            TotalCOOPIPSearchesOutToAMR=COOPIPSearchesOutToAMR + COOPIPSearchesWaitingToGoOutToAMR
            if(TotalCOOPIPSearchesOutToAMR > TotalSearchCap):
                print str(TotalCOOPIPSearchesOutToAMR) + ' Total COOPIP Searches Out was too high.  :  ' + str(substation.Name) + "  Searches Out to AMR:" + str(COOPIPSearchesOutToAMR) + '  Waiting to go out:  ' + str(COOPIPSearchesWaitingToGoOutToAMR)
                continue
            MeterSearchesToGoOut=(MeterSearch.objects.filter(CompleteDateTime__isnull=True).exclude(CurrentlyOutToAMRIndicator=True).exclude(SubstationNamesAlreadySearchedList__contains=substation.Name
                                   ).order_by('-PriorityLevel'))
            count=0
            for i in MeterSearchesToGoOut:
                if count > SearchesToGoOutCap:
                    break
                if(i.PrioritySubstation and not i.PrioritySubstation == substation):
                    continue
                print "Sending Out:  " + str(i.id)
                count+=1
                new = MeterSubstationSearch()
                new.MeterSearch=i
                new.Substation=substation
                new.StartDateTime=datetime.datetime.now()
                new.WaitingToGoOutToAMRIndicator=True
                new.save()
                i.OutToAMRCount=i.OutToAMRCount + 1
                i.PriorityLevel=i.PriorityLevel + 1
                i.CurrentlyOutToAMRIndicator=True
                i.LastOutToAMRDateTime=new.StartDateTime
                i.save()
    def CancelMeterSearchesWithTooManyAttempts(self):
        MeterSearches=MeterSearch.objects.filter(OutToAMRCount__gte=25).filter(CompleteDateTime__isnull=True)
        for i in MeterSearches:
            i.CancelMeterSearch()
    def CancelMeterSearch(self):
        if(self.CompleteDateTime):
            return
        self.CurrentlyOutToAMRIndicator=False
        self.FinalResultMeterNotFoundIndicator=True
        self.FinalResultMeterAddedIndicator=False
        self.FinalResultMeterResearchedIndicator=False
        self.CompleteDateTime=datetime.datetime.now()
        self.save()
        MeterSubstationSearches=MeterSubstationSearch.objects.filter(MeterSearch=self).filter(OutToAMRIndicator=True)
        for i in MeterSubstationSearches:
            i.CompleteDateTime=datetime.datetime.now()
            i.OutToAMRIndicator=False
            i.MeterSearchNeedsToBeUpdatedIndicator=False
            i.save()





class MeterSubstationSearch(models.Model):
    '''Actual search on a single substation '''
    using='default'
    MeterSearch=models.ForeignKey('MeterSearch', db_column='metersearch')
    Substation=models.ForeignKey('Substation', db_column='substation')
    StartDateTime=models.DateTimeField(db_column='startdatetime')
    CompleteDateTime=models.DateTimeField(db_column='completedatetime', null=True, blank=True)
    WaitingToGoOutToAMRIndicator=models.NullBooleanField(db_column='waitingtogoouttoamrindicator', null=True, blank=True)
    OutToAMRIndicator=models.NullBooleanField(db_column='outtoamrindicator', null=True, blank=True)
    OutToAMRDateTime=models.DateTimeField(db_column='outtoamrdatetime', null=True, blank=True)
    AMRMeterNotFoundIndicator=models.NullBooleanField(db_column='amrmeternotfoundindicator', null=True, blank=True)
    AMRNewMeterFoundIndicator=models.NullBooleanField(db_column='amrnewmeterfoundindicator', null=True, blank=True)
    AMRMeterResearchedIndicator=models.NullBooleanField(db_column='amrmeterresearchedindicator', null=True, blank=True)
    MeterSearchNeedsToBeUpdatedIndicator=models.NullBooleanField(db_column='metersearchneedstobeupdatedindicator', null=True, blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'metersubstationsearch'
        app_label='djangoproject'
    def __unicode__(self):
        ReturnString=str(self.MeterSearch.SerialNumber) + ':'  + self.Substation.Name + ':'
        if self.AMRMeterNotFoundIndicator:
            ReturnString+='Not Found'
        elif self.AMRNewMeterFoundIndicator:
            ReturnString+='New'
        elif self.AMRMeterResearchedIndicator:
            ReturnString+='Researched'
        return ReturnString
    def Update(self):
        import django.db
        if(self.CompleteDateTime):
            return  #Do Nothing if already Completed
        if(not self.MeterSearch.LastOutToAMRDateTime):  #Prevents an error if a reset is performed on searches that are still out to AMR
            self.MeterSearch.LastOutToAMRDateTime = datetime.datetime.now()
            self.MeterSearch.save()

        if (datetime.datetime.now() - self.MeterSearch.LastOutToAMRDateTime) > datetime.timedelta(hours=0, minutes=30) and self.OutToAMRIndicator:
            #Reduced timeout to 30 minutes w/ new AMR Server
            metersearch=self.MeterSearch
            metersearch.PriorityLevel=metersearch.PriorityLevel-10
            metersearch.Substation=None
            metersearch.OutToAMRCount=metersearch.OutToAMRCount + 1
            metersearch.CurrentlyOutToAMRIndicator=False
            metersearch.LastOutToAMRDateTime=datetime.datetime.now()
            metersearch.save()
            try:
                AMR.Tempserialnumber.objects.filter(serialnumber=self.MeterSearch.SerialNumber).delete()
                django.db.connections['amr'].commit()
            except:
                pass
            self.delete()  #Delete, as this represents a time-out condition
            return

        try:
            log=amrcache.AMRSearchLogFile.objects.filter(SerialNumber=self.MeterSearch.SerialNumber).filter(CompleteDateTime__gte=self.StartDateTime).order_by('-CompleteDateTime')[0]
        except:
            return
        self.OutToAMRIndicator=False
        self.AMRNewMeterFoundIndicator=log.AddIndicator
        self.AMRMeterResearchedIndicator=log.ModIndicator
        self.AMRMeterNotFoundIndicator=log.MeterNotFoundIndicator
        self.CompleteDateTime=datetime.datetime.now()
        self.MeterSearchNeedsToBeUpdatedIndicator=True
        self.save()
        if(self.AMRMeterNotFoundIndicator):  #cleanup the AMR Tempserialnumber table of failed searches
            try:
                AMR.Tempserialnumber.objects.filter(serialnumber=self.MeterSearch.SerialNumber).delete()
                django.db.connections['amr'].commit()
            except:
                pass
    def SendSearchesToAMR(self):
        ''' Sends All Searches waiting to go out to AMR to AMR in a Batch File'''
        
        OutgoingSearchQueue = list(MeterSubstationSearch.objects.filter(WaitingToGoOutToAMRIndicator=True))  #List so that we can append fake searches if necessary
        if len(OutgoingSearchQueue) == 0:
            self.SendUploadBatchSearchToAMR(djangoproject.settings.AMRMeterSearchBatchFileName)  #try to send the file in case ftp problems prevented it from going out last time
            return
        while len(OutgoingSearchQueue) < 4:  #Minimum of 4 searches at a time must be in the batch file
            OutgoingSearchQueue.append(FakeMeterSearch(djangoproject.settings.FakeMeterSerialNumbers[len(OutgoingSearchQueue)]))
        BatchFileWrite=open(djangoproject.settings.AMRMeterSearchBatchFileName, 'wa')
        for search in OutgoingSearchQueue:
            try:
                SearchSerialNumber=search.MeterSearch.SerialNumber
                FakeSearch=False
            except AttributeError: #fake meter search
                SearchSerialNumber=search.SerialNumber
                FakeSearch=True
            if FakeSearch:
                BatchFileWrite.write('BATCH,MOD,PATH,' + str(SearchSerialNumber) + ',' + search.Substation.Name.upper() + ',ALL,ALL,ALL,BOTH,ALL\n')
                continue
            AMRMeter=AMR.Serialnumber.objects.filter(serialnumber=SearchSerialNumber)
            if not AMRMeter.count():
                BatchFileWrite.write('BATCH,ADD,PATH,' + str(SearchSerialNumber) + ',' + search.Substation.Name.upper() + ',ALL,ALL,ALL,BOTH,ALL\n')
                BatchFileWrite.write('BATCH,ADD,SNDATA,' + str(SearchSerialNumber) + ', , , , , , , , ,Y, , , , , , , \n')#active flag
                BatchFileWrite.write('BATCH,ADD,PORT,' + str(SearchSerialNumber) + ',0, , , , , ,Y, , , , , \n')
                BatchFileWrite.write('BATCH,ADD,METER,' + str(SearchSerialNumber) + ',0, , , ,' + str(SearchSerialNumber) +', , , , ,60, , , \n')
            else:
                BatchFileWrite.write('BATCH,MOD,PATH,' + str(SearchSerialNumber) + ',' + search.Substation.Name.upper() + ',ALL,ALL,ALL,BOTH,ALL\n')
            #BATCH, ADD/MOD, PATH, MeterDCSINumber, Substation, Bus, Feeder, Phase, Signal, Detection
            #BatchFileWrite.write('BATCH,MOD,PATH,' + str(SearchSerialNumber) + ',' + search.Substation.Name.upper() + ',ALL,ALL,ALL,BOTH,ALL\n')
            search.WaitingToGoOutToAMRIndicator = False
            search.OutToAMRIndicator = True
            search.OutToAMRDateTime=datetime.datetime.now()
            search.save()
        BatchFileWrite.close()
        self.SendUploadBatchSearchToAMR(djangoproject.settings.AMRMeterSearchBatchFileName)
    def SendUploadBatchSearchToAMR(self, BatchFileName):
        if not os.path.exists(BatchFileName):
            return
        ftp = FTP(djangoproject.settings.AMRIPAddress)
        ftp.login(djangoproject.settings.AMRBatchUserName,djangoproject.settings.AMRBatchPassword)
        FileList = []
        ftp.retrlines('NLST',FileList.append)
        for name in FileList:
            if '.DAT' in name: #AMR Batch server process has not picked up previous queue out
                ftp.quit()
                return
        AMRFileName='COOPIP_DYNAMICBATCH'
        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        try:  #Attempt to rename any existing completed uploaded files to reflect the current date/time
            ftp.rename(AMRFileName + '.TAD','COOPIP_' + timestamp + '.TAD')
            ftp.rename(AMRFileName + '.LOG','COOPIP_' + timestamp + '.LOG')
        except:
            pass
        BatchFileRead=open(BatchFileName, 'r')
        ftp.storlines('STOR ' + AMRFileName + '.DAT', BatchFileRead)
        BatchFileRead.close()
        try:
            RemoteFileSize=ftp.size(AMRFileName + '.DAT')
        except:
            RemoteFileSize=0
        if RemoteFileSize > 20:
            os.remove(BatchFileName)  #make sure that the file was uploaded and has a reasonable remote size before deleting the local file
        ftp.quit()

class FakeMeterSearch():
    """FakeMeterSearch is used to pad the batch file to at least 4 entries.  AMR does not support smaller batch files"""
    def __init__(self,SerialNumber):
        self.SerialNumber=SerialNumber
        self.Substation=amrcache.Substation.objects.filter(Name='UNKNOWN')[0]
    def save(self):
        pass












class MeterPing(models.Model):
    """MeterPing is a list of the ping results for meters that have been pinged"""
    using='default'
    Rstns=models.IntegerField(null=True, blank=True, db_column='rstns')
    Rssce=models.IntegerField(null=True, blank=True, db_column='rssce')
    Rsrce=models.IntegerField(null=True, blank=True, db_column='rsrce')
    Outsub=models.IntegerField(null=True, blank=True, db_column='outsub')
    Outbus=models.IntegerField(null=True, blank=True, db_column='outbus')
    Outfdr=models.IntegerField(null=True, blank=True, db_column='outfdr')
    Outphs=models.IntegerField(null=True, blank=True, db_column='outphs')
    Outsig=models.IntegerField(null=True, blank=True, db_column='outsig')
    Outdet=models.IntegerField(null=True, blank=True, db_column='outdet')
    SuccessIndicator=models.NullBooleanField(null=True, blank=True, db_column='successindicator')
    SerialNumber=models.IntegerField(null=True, blank=True, db_column='serialnumber')
    DateTime=models.DateTimeField(db_column='datetime', db_index=True)  #StampDateTime from AMR
    objects=models.GeoManager()
    class Meta:
        db_table=u'meterping'
        app_label='djangoproject'
    def GetHeadings(self):
        Width=10
        return ('SerialNumber'.ljust(25) + 'Date/Time'.ljust(25) + 'Rstns'.ljust(Width) + 'Rssce'.ljust(Width) + 'Rsrce'.ljust(Width) + 'Outsub'.ljust(Width) +
                'Outbus'.ljust(Width) + 'Outfdr'.ljust(Width) +
                'Outphs'.ljust(Width) + 'Outsig'.ljust(Width) + 'Outdet'.ljust(Width))
    def __unicode__(self):
        Width=10
        return (str(self.SerialNumber).ljust(25) + str(self.DateTime).ljust(25) + str(self.Rstns).ljust(Width) +
                str(self.Rssce).ljust(Width) + str(self.Rsrce).ljust(Width) + str(self.Outsub).ljust(Width) +
                str(self.Outbus).ljust(Width) +
                str(self.Outfdr).ljust(Width) + str(self.Outphs).ljust(Width) + str(self.Outsig).ljust(Width) + str(self.Outdet).ljust(Width))
    def GetLatestFromAMR(self):
        if(MeterPing.objects.all().count()):  #Determine when the last meterping was downloaded
            meterPing=MeterPing.objects.all().order_by('-DateTime')[0]
            PreviousDownloadDateTime=meterPing.DateTime-datetime.timedelta(hours=0)
            PreviousDownloadDateTime=str(str(PreviousDownloadDateTime.year) + '-' + str(PreviousDownloadDateTime.month).rjust(2, '0') + '-' + str(PreviousDownloadDateTime.day).rjust(2, '0') + ' ' +
                 str(PreviousDownloadDateTime.hour).rjust(2, '0') + ':' + str(PreviousDownloadDateTime.minute).rjust(2, '0') + ':' + str(PreviousDownloadDateTime.second).rjust(2, '0'))
            print PreviousDownloadDateTime
        else:
            PreviousDownloadDateTime=datetime.datetime.now() - datetime.timedelta(hours=24)  #For 1st download, get results up to a day old
        AMRResults=list(AMR.Tctpingresplog.objects.filter(stamp__gt=PreviousDownloadDateTime).order_by('-stamp'))
        for result in AMRResults:
            NewMeterPing=MeterPing()
            NewMeterPing.SerialNumber=result.serialnumber
            NewMeterPing.Rstns=result.rstns
            NewMeterPing.Rssce=result.rssce
            NewMeterPing.Rsrce=result.rsrce
            NewMeterPing.Outsub=result.outsub
            NewMeterPing.Outbus=result.outbus
            NewMeterPing.Outfdr=result.outfdr
            NewMeterPing.Outphs=result.outphs
            NewMeterPing.Outsig=result.outsig
            NewMeterPing.Outdet=result.outdet
            NewMeterPing.DateTime=result.stamp
            if(NewMeterPing.Rstns == 0) and (NewMeterPing.Rssce == 0) and (NewMeterPing.Rsrce == 0):
                NewMeterPing.SuccessIndicator=True
            else:
                NewMeterPing.SuccessIndicator=False
            NewMeterPing.save()
    def CleanupAMRPingResponses(self):
        if(AMR.Tctpingresplog.objects.all().count() > 2000):
            print "Deleting 500 pings"
            print datetime.datetime.now()
            Oldest=AMR.Tctpingresplog.objects.all().order_by('stamp')[:500]
            LatestToKeep=Oldest[499].stamp
            AMR.Tctpingresplog.objects.filter(stamp__lte=LatestToKeep).delete()
            django.db.connections['amr'].commit()
            print datetime.datetime.now()
    def PingWaitingMeters(self):
        import django.db
        amrcache.Substation().RefreshCache()
        MetersToPing=Meter.objects.filter(SendPingToAMRIndicator=True)
        if(not MetersToPing):
            return
        if(AMR.TctInpq.objects.all().count() > 500):
            return  #Sanity check on Tct_inpq, it has no rows if the AMR server is processing all incoming commands
        SQLList=[]
        MetersToPingDictBySub={}
        for substation in amrcache.Substation.objects.all():
            MetersToPingDictBySub[substation.id] = 0
        Now = datetime.datetime.now()
        for meter in MetersToPing:
            try:
                SubstationStatus=meter.Substation.AMRSubstationStatus
            except:
                meter.SendPingToAMRIndicator=False
                meter.save()
                continue
            if(not SubstationStatus > 0):
                continue #Skip pinging any meter with a SubstationStatus > 0
            
            if((MetersToPingDictBySub[meter.Substation.id] + meter.Substation.AMRCommandsOut) < djangoproject.settings.MeterPingMaximumOutCommands and 
                   (Now - meter.Substation.LastUpdateDateTime).total_seconds() < 90):
                SQLList.append("INSERT INTO TCT_INPQ(SUBSYSID, PRI, CMDDATA) VALUES(45, 1, 'TCTPING|" + str(meter.SerialNumber) + "|');")
                #We Should be able to use Initiate Outage Detection Event Request through the Optimum Server
                #It would send back an Outage Detection Event Notification []possibly]?
                MetersToPingDictBySub[meter.Substation.id] += 1
                meter.LastPingOutDateTime=datetime.datetime.now()
                meter.SendPingToAMRIndicator=False
                meter.save()
        SQLString=''.join(SQLList)
        Cursor = django.db.connections['amr'].cursor()
        for sqlstatement in SQLList:
            print sqlstatement
            Cursor.execute(sqlstatement)
        try:
            django.db.connections['amr'].commit()
        except:
            pass
    @staticmethod
    def UpdateMetersWithLatestResults():
        if(not Meter.objects.all().count()):
            return []
        NewMeterPingFailureList=[]
        meter=Meter.objects.all().exclude(LastPingResponseDateTime__isnull=True).order_by('-LastPingResponseDateTime')[0]
        if(meter.LastPingResponseDateTime):
            StartDateTime=meter.LastPingResponseDateTime
        else:
            StartDateTime=datetime.datetime.today() - datetime.timedelta(days=1)
        MeterPings=MeterPing.objects.filter(DateTime__gte=StartDateTime).order_by('DateTime')
        Count = 0
        for meterPing in MeterPings:
            if(Count % 100) == 0:
                print "meterPing Date Time:  " + str(meterPing.DateTime).ljust(30) + 'SerialNumber:  ' + str(meterPing.SerialNumber)
            Count = Count + 1
            meter = Meter.objects.filter(SerialNumber=meterPing.SerialNumber)
            if(not meter):
                continue
            meter = meter[0]
            meter.LastPingResponseDateTime=meterPing.DateTime
            if(not meterPing.SuccessIndicator and (not meter.LastPingSuccessIndicator==meterPing.SuccessIndicator)):
                NewMeterPingFailureList.append(meter)
            meter.LastPingSuccessIndicator=meterPing.SuccessIndicator
            if(meterPing.SuccessIndicator):
                meter.LastSuccessfulPingDateTime=meterPing.DateTime
            meter.save()
            meter.CalculateSymbology()
            meter.CalculateIncidentSymbology()
        return NewMeterPingFailureList
    @staticmethod
    def GetLatestCOOPTalkSuccessReads():
        """Gets the most recent COOPTalk InResponseReadingChangedNotificationIndicator and sets them as successful Meter Pings"""
        COOPTalks = djangoproject.models.cooptalk.COOPTalk.objects.filter(InResponseReadingChangedNotificationIndicator=True).exclude(LocalProcessingCompleteIndicator=True)
        for i in COOPTalks:
            pass


class OutdoorLightingConversion(models.Model):
    """OutdoorLightingConversion is a model to track the progress of outdoor lighting conversions (adding location data to lights in our CIS) after data has been field collected using the GIS."""
    using='default'
    #This data is pulled from the GIS
    gisid = models.IntegerField(db_column='gisid')
    gispoint = models.PointField(db_column='gispoint', srid=2273, null=True, blank=True)
    gisstationidentifier = models.TextField(db_column='gisstationidentifier', null=True, blank=True)
    gistagnumber = models.TextField(db_column='gistagnumber', null=True, blank=True)
    gisoutdoorlightmemberdescription = models.TextField(db_column='gisoutdoorlightmemberdescription', null=True, blank=True)
    gisoutdoorlightmapnumber = models.TextField(db_column='gisoutdoorlightmapnumber', null=True, blank=True)
    gisoutdoorlightmeternumber = models.TextField(db_column='gisoutdoorlightmeternumber', null=True, blank=True)
    gisoutdoorlightmetermapnumber = models.TextField(db_column='gisoutdoorlightmetermapnumber', null=True, blank=True)
    gisoutdoorlightidleindicator = models.NullBooleanField(db_column='gisoutdoorlightidleindicator', null=True, blank=True)
    gisneedsretirementindicator = models.NullBooleanField(db_column='gisneedsretirementindicator', null=True, blank=True)
    gisoutdoorlightadditionalbillingpolecount = models.IntegerField(db_column='gisoutdoorlightadditionalbillingpolecount', null=True, blank=True)

    gisoutdoorlightratename = models.TextField(db_column='gisoutdoorlightratename', null=True, blank=True)
    gisoutdoorlightcustomerownspoleindicator = models.NullBooleanField(db_column='gisoutdoorlightcustomerownspoleindicator', null=True, blank=True)
    gisoutdoorlightcustomerownslightindicator = models.NullBooleanField(db_column='gisoutdoorlightcustomerownslightindicator', null=True, blank=True)
    gispolecompatibleunitname = models.TextField(db_column='gispolecompatibleunitname', null=True, blank=True)
    giscreatedatetime = models.DateTimeField(db_column='giscreatedatetime', null=True, blank=True)
    giscreateusername = models.TextField(db_column='giscreateusername', null=True, blank=True)
    giscircuitnumbers = models.TextField(db_column='giscircuitnumbers', null=True, blank=True)
    gisoutdoorlightconsumerownedlightcount = models.IntegerField(db_column='gisoutdoorlightconsumerownedlightcount', null=True, blank=True)
    gissubstation = models.TextField(db_column='gissubstation', null=True, blank=True)
    #this data is pulled from the CIS upon creation of a new OutdoorLightingConversion Object
    cismeterdistrict = models.TextField(db_column='cismeterdistrict', null=True, blank=True)
    cismetertaxdistrict = models.TextField(db_column='cismetertaxdistrict', null=True, blank=True)
    cismetercountycode = models.TextField(db_column='cismetercountycode', null=True, blank=True)
    cismetercycle = models.TextField(db_column='cismetercycle', null=True, blank=True)
    cismemberseperator = models.TextField(db_column='cismemberseperator', null=True, blank=True)
    cislocation = models.TextField(db_column='cislocation', null=True, blank=True)

    #This data is entered by the Billing Setup User
    CISLocationSetupCompleteIndicator=models.NullBooleanField(db_column='cislocationsetupcompleteindicator', null=True, blank=True)
    LightsOriginallyInCIS=models.IntegerField(db_column='lightsoriginallyincis', null=True, blank=True)
    LightsFinallyInCIS=models.IntegerField(db_column='lightsfinallyincis', null=True, blank=True)
    CISMemberSeparatorSetupCompleteIndicator=models.NullBooleanField(db_column='cismemberseparatorsetupcompleteindicator', null=True, blank=True)
    BillingSetupCompleteIndicator=models.NullBooleanField(db_column='billingsetupcompleteindicator', null=True, blank=True)
    BillingSetupCompleteDateTime=models.DateTimeField(db_column='billingsetupcompletedatetime', null=True, blank=True)
    BillingSetupCompleteUsername=models.TextField(db_column='billingsetupcompleteusername', null=True, blank=True)
    SendRetirementToGISIndicator=models.NullBooleanField(db_column='sendretirementtogisindicator', null=True, blank=True)
    OutdoorLightBillingPhaseIndicator=models.NullBooleanField(db_column='outdoorlightbillingphaseindicator', null=True, blank=True)
    OutdoorLightGISPhaseIndicator=models.NullBooleanField(db_column='outdoorlightgisphaseindicator', null=True, blank=True)
    DeleteIndicator=models.NullBooleanField(db_column='deleteindicator', null=True, blank=True)


    CreateDateTime=models.DateTimeField(db_column='createdatetime')  
    objects=models.GeoManager()
    class Meta:
        db_table=u'outdoorlightingconversion'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.id)

    def UpdateFieldsFromCIS(self):
        if(self.cismemberseperator):
            print "Not Updating :  " + str(self.id) + "  due to already having a cismemberseperator"
            return 
        meterDetl = CIS.Meterdetl.objects.filter(idlecode='00').filter(meter=str(self.gisoutdoorlightmeternumber).rjust(12, '0'))
        if(meterDetl):
            location = meterDetl[0].location
            locinfodetl = CIS.Locinfodetl.objects.filter(location=location)
            if(locinfodetl):
                self.cismeterdistrict = locinfodetl[0].dist
                self.cismetertaxdistrict = locinfodetl[0].taxdistcode
                self.cismetercountycode = locinfodetl[0].countycode
                self.cismetercycle = locinfodetl[0].cycle
                self.cismemberseperator=meterDetl[0].mbrsep.lstrip('0')
                self.cislocation=meterDetl[0].location.lstrip('0')
        else:
            self.cismemberseperator=''
            self.cislocation=''
        self.save()            
            




class OutdoorLightingConversionHistoricalReport(models.Model):
    """OutdoorLightingConversionHistoricalReport is a model to track the progress of outdoor lighting conversions, 
       pulling daily data from the CIS on how many lights are in the Yard Light and how many are in the outdoor lighting system"""
    using='default'
    #This data is pulled from the GIS
    TotalGISLightAssemblies = models.IntegerField(null=True, blank=True, db_column='totalgislightassemblies')
    TotalGISOutdoorLightConversion = models.IntegerField(null=True, blank=True, db_column='totalgisoutdoorlightconversion')
    TotalGISOutdoorLightConversionNeedRetirement = models.IntegerField(null=True, blank=True, db_column='totalgisoutdoorlightconversionneedretirement')
    TotalGISOutdoorLightConversionRetirementComplete = models.IntegerField(null=True, blank=True, db_column='totalgisoutdoorlightconversionretirementcomplete')
    TotalBillingOutdoorLightConversionComplete = models.IntegerField(null=True, blank=True, db_column='totalbillingoutdoorlightconversioncomplete')
    #This data is pulled from COOPIP
    GeneralStatisticIndicator=models.NullBooleanField(null=True, blank=True, db_column='generalstatisticindicator')
    #This data is pulled from the CIS
    YardLightRate = models.IntegerField(null=True, blank=True, db_column='yardlightrate')
    YardLightType = models.IntegerField(null=True, blank=True, db_column='yardlighttype')
    Quantity = models.IntegerField(null=True, blank=True, db_column='quantity')
    YardLightIndicator=models.NullBooleanField(null=True, blank=True, db_column='yardlightindicator')
    OutdoorLightIndicator=models.NullBooleanField(null=True, blank=True, db_column='outdoorlightindicator')
    DateTime=models.DateTimeField(db_column='createdatetime')  
    objects=models.GeoManager()
    class Meta:
        db_table=u'outdoorlightingconversionhistoricalreport'
        app_label='djangoproject'
    @staticmethod 
    def AddToReportDictionary(Dict, Rate, Type, Quantity):
        if(not Dict.has_key(Rate)):
            Dict[Rate] = {}
        if(not Dict[Rate].has_key(Type)):
            Dict[Rate][Type] = 0
        Dict[Rate][Type] = Dict[Rate][Type] + Quantity
    
    @staticmethod 
    def GetYardLightTotalsFromCIS():
        Today=datetime.datetime.now()
        StartDate = datetime.datetime(year=Today.year, month=Today.month, day=Today.day)
        EndDate = StartDate + datetime.timedelta(days=1)
        Locations = list(CIS.Locinfodetl.objects.filter(idlecode='00'))  
        YardLightDict = {}
        for location in Locations:
            YardLightNumber = 1
            while (YardLightNumber < 5):
                YardLightString = str(eval("location.yardlightinfo_00" + str(YardLightNumber))[:20])
                YardLightNumber = YardLightNumber + 1
                Quantity = int(YardLightString[10:14])
                if(not Quantity):
                    continue
                Rate = int(YardLightString[:2])
                Type = int(YardLightString[2:4])
                OutdoorLightingConversionHistoricalReport.AddToReportDictionary(YardLightDict, Rate, Type, Quantity)          

        for ratekey in YardLightDict:
            for typekey in YardLightDict[ratekey]:
                print str(ratekey).ljust(10) + str(typekey).ljust(10) + str(YardLightDict[ratekey][typekey])
                ExistingRecords=OutdoorLightingConversionHistoricalReport.objects.filter(DateTime__gte=StartDate).filter(DateTime__lt=EndDate).filter(YardLightRate=ratekey).filter(YardLightType=typekey).filter(YardLightIndicator=True)
                if(ExistingRecords):
                    New = ExistingRecords[0]
                else:
                    New = OutdoorLightingConversionHistoricalReport()
                    New.YardLightRate=ratekey
                    New.YardLightType=typekey
                    New.YardLightIndicator=True
                New.DateTime=Today
                New.Quantity=YardLightDict[ratekey][typekey]
                New.save()        
    @staticmethod 
    def GetOutdoorLightTotalsFromCIS():
        Today=datetime.datetime.now()
        StartDate = datetime.datetime(year=Today.year, month=Today.month, day=Today.day)
        EndDate = StartDate + datetime.timedelta(days=1)
        OutdoorLights=list(CIS.Seclightdetl.objects.all().filter(idlecode='00'))
        OutdoorLightDict = {}
        for outdoorLight in OutdoorLights:
            Quantity = 1
            Rate = int(str(outdoorLight.ylrate))
            Type = int(str(outdoorLight.yltype))
            OutdoorLightingConversionHistoricalReport.AddToReportDictionary(OutdoorLightDict, Rate, Type, Quantity)          
            
        for ratekey in OutdoorLightDict:
            for typekey in OutdoorLightDict[ratekey]:
                print str(ratekey).ljust(10) + str(typekey).ljust(10) + str(OutdoorLightDict[ratekey][typekey])
                ExistingRecords=OutdoorLightingConversionHistoricalReport.objects.filter(DateTime__gte=StartDate).filter(DateTime__lt=EndDate).filter(YardLightRate=ratekey).filter(YardLightType=typekey).filter(OutdoorLightIndicator=True)
                if(ExistingRecords):
                    New = ExistingRecords[0]
                else:
                    New = OutdoorLightingConversionHistoricalReport()
                    New.YardLightRate=ratekey
                    New.YardLightType=typekey
                    New.OutdoorLightIndicator=True
                New.DateTime=Today
                New.Quantity=OutdoorLightDict[ratekey][typekey]
                New.save()
    @staticmethod
    def GetGeneralStatistics():
        Today=datetime.datetime.now()
        StartDate = datetime.datetime(year=Today.year, month=Today.month, day=Today.day)
        EndDate = StartDate + datetime.timedelta(days=1)
        ExistingRecords=OutdoorLightingConversionHistoricalReport.objects.filter(
                        DateTime__gte=StartDate).filter(DateTime__lt=EndDate).filter(GeneralStatisticIndicator=True)
        if ExistingRecords:
            New = ExistingRecords[0]
        else:
            New = OutdoorLightingConversionHistoricalReport()
            New.GeneralStatisticIndicator=True
        New.DateTime=Today
        TotalGISLightAssemblies = GIS.Coopgisassembly.objects.filter(compatibleunit__lightindicator=True).count()
        New.TotalGISLightAssemblies=TotalGISLightAssemblies

        OutdoorLighting = GIS.Coopgiscondition.objects.filter(priorityoutdoorlightingconversionindicator=True).exclude(deletedindicator=True)
        GISOutdoorLightConversion = OutdoorLighting.count()
        New.TotalGISOutdoorLightConversion = GISOutdoorLightConversion

        NeedRetirement = OutdoorLighting.filter(needsretirementindicator=True).exclude(retirementcompleteindicator=True).count()
        New.TotalGISOutdoorLightConversionNeedRetirement = NeedRetirement

        RetirementComplete = OutdoorLighting.filter(retirementcompleteindicator=True).count()
        New.TotalGISOutdoorLightConversionRetirementComplete = RetirementComplete

        COOPIPBillingOutdoorLightConversion = OutdoorLightingConversion.objects.filter(BillingSetupCompleteIndicator=True).count()
        New.TotalBillingOutdoorLightConversionComplete = COOPIPBillingOutdoorLightConversion
        New.save()
        


class COOPIPSetting(models.Model):
    """COOPIPSetting is a model to set global changable settings for COOPIP, such as the rate of automated meter pinging."""
    using='default'
    Name = models.TextField(db_column='name', blank=True)  #Valid Names:  'AutomatedMeterPingRate'
    Value = models.TextField(db_column='value', blank=True)
    Username = models.TextField(db_column='username', blank=True)
    CreateDateTime = models.DateTimeField(db_column='createdatetime', blank=True)
    objects=models.GeoManager()
    class Meta:
        db_table=u'coopipsetting'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.id)

    @staticmethod
    def SetPingRate(AutomatedMeterPingRate, Username):  
        if not (AutomatedMeterPingRate in ['off', 'low' ,'normal', 'high']):
            raise(Exception('Invalid Automated Meter Ping Rate:  ' + str(AutomatedMeterPingRate)))
        New=COOPIPSetting()
        New.Name='AutomatedMeterPingRate'
        New.Value=AutomatedMeterPingRate
        New.Username=Username
        New.CreateDateTime=datetime.datetime.now()
        New.save()
    @staticmethod
    def GetPingRate():
        PingRates = COOPIPSetting.objects.filter(Name='AutomatedMeterPingRate').order_by('-id')
        if(PingRates.count()):
            return PingRates[0].Value
        else:
            return 'low'
        


class MeterPingDynamicCircuit(models.Model):
    """MeterPingDynamicCircuit is a model to meter pinging to try cycling through alternative circuits when circuits are back-fed"""
    using='default'
    NormalGISCircuitNumber=models.TextField(db_column='normalgiscircuitnumber', blank=True, null=True)
    AlternativeGISCircuitNumberList=models.TextField(db_column='alternativegiscircuitnumberlist', blank=True, null=True)
    objects=models.GeoManager()
    def UpdateMetersWithAlternativeCircuits(self):
        Meters=Meter.objects.filter(GISCircuitNumber = self.NormalGISCircuitNumber)
        for meter in Meters: #Set all meters to a valid state to be ready to switch circuits if a ping fails.
            if(meter.DynamicCircuitActivatedIndicator):
                continue
            meter.DynamicCircuitActivatedIndicator = True
            meter.DynamicCircuitOriginalAMRSubid=meter.AMRSubid
            meter.DynamicCircuitOriginalAMRBusid=meter.AMRBusid
            meter.DynamicCircuitOriginalAMRFdrid=meter.AMRFdrid
            meter.DynamicCircuitCurrentAMRSubid=meter.AMRSubid
            meter.DynamicCircuitCurrentAMRBusid=meter.AMRBusid
            meter.DynamicCircuitCurrentAMRFdrid=meter.AMRFdrid
            meter.save()
    def ClearMetersWithAlternativeCircuits(self):
        Meters=Meter.objects.filter(GISCircuitNumber = self.NormalGISCircuitNumber)
        for meter in Meters:
            meter.DynamicCircuitActivatedIndicator = False
            meter.DynamicCircuitCurrentAMRSubid=meter.AMRSubid
            meter.DynamicCircuitCurrentAMRBusid=meter.AMRBusid
            meter.DynamicCircuitCurrentAMRFdrid=meter.AMRFdrid
            temp=meter.DynamicCircuitOriginalAMRSubid
            meter.Substation = amrcache.Substation.objects.filter(AMRsubid=meter.DynamicCircuitOriginalAMRSubid)[0]
            meter.save()
            meter.UpdateAMRDynamicMeterCircuit()
    @staticmethod
    def GetPossibleCircuitList(meter):
        MeterPingDynamicCircuits=MeterPingDynamicCircuit.objects.filter(NormalGISCircuitNumber=meter.GISCircuitNumber).order_by('id')
        Results=[]
        Results.append(meter.GISCircuitNumber)
        for i in MeterPingDynamicCircuits:
            for j in i.AlternativeGISCircuitNumberList.split(','):
                Results.append(i.AlternativeGISCircuitNumberList.strip())
        return Results
    class Meta:
        db_table=u'meterpingdynamiccircuit'
        app_label='djangoproject'
    def __unicode__(self):
        return str(self.id)
     

class GISToAMRCircuitMap(models.Model):
    """Maps from a GIS Circuit Number to an AMR Circuit"""
    using='default'
    GISCircuitNumber=models.TextField(db_column='giscircuitnumber', blank=True, null=True)
    AMRSubid=models.TextField(db_column='amrsubid', null=True, blank=True)
    AMRBusid=models.TextField(db_column='amrbusid', null=True, blank=True)
    AMRFdrid=models.TextField(db_column='amrfdrid', null=True, blank=True)   
    @staticmethod 
    def UpdateGISToAMRCircuitMap():
        GISToAMRCircuitMap.objects.all().delete()
        for i in djangoproject.settings.SubstationMappingList:
            New=GISToAMRCircuitMap()
            New.GISCircuitNumber=i[0]
            New.AMRSubid=i[1]
            New.AMRBusid=i[2]
            New.AMRFdrid=i[3]
            New.save()
    @staticmethod
    def GetGISCircuitNumber(AMRSubid, AMRBusid, AMRFdrid):
        gISToAMRCircuitMap = GISToAMRCircuitMap.objects.filter(AMRSubid=AMRSubid).filter(AMRBusid=AMRBusid).filter(AMRFdrid=AMRFdrid)
        if(len(gISToAMRCircuitMap) == 1):
            return gISToAMRCircuitMap[0].GISCircuitNumber
        return 'UNKNOWN'
    @staticmethod
    def GetAMRCircuitDataList(GISCircuitNumber):
        gisToAMRCircuitMap = GISToAMRCircuitMap.objects.filter(GISCircuitNumber=GISCircuitNumber)
        if(len(gISToAMRCircuitMap) == 1):
             return gISToAMRCircuitMap[0].AMRSubid, gISToAMRCircuitMap[0].AMRBusid, gISToAMRCircuitMap[0].AMRFdrid
        return 'UNKNOWN', 'UNKNOWN', 'UNKNOWN'
    class Meta:
        db_table=u'gistoamrcircuitmap'
        app_label='djangoproject'
    def __unicode__(self):
        return str(str(self.id).ljust(10) + str(self.GISCircuitNumber).ljust(10) + str(self.AMRSubid).ljust(10) + str(self.AMRBusid).ljust(10) + str(self.AMRFdrid).ljust(10))

